/*
Copyright (C) 2011 Ing. Marek Hubal

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Other sources
paq9a archiver, Dec. 31, 2007 (C) 2007, Matt Mahoney
*/

#include <windows.h>
#include "cbi_core.h"
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>

#pragma warning (disable: 4996)

#include "cbi_core2.h"

#pragma pack(1)
typedef struct {
	BYTE Magic1;
	BYTE Magic2;
	UINT32 FileSize;
	UINT16 Reserver1;
	UINT16 Reserver2;
	UINT32 BmpOffset;
	UINT32 HeaderSize;
	INT32 Width;
	INT32 Height;
	UINT16 NPlanes;
	UINT16 BitsPP;
	UINT32 CompressType;
	UINT32 BmpByteSize;
	INT32 HRes;
	INT32 VRes;
	UINT32 NumOfColors;
	UINT32 NumOfImpColors;
} BMP_FILE_HEADER, *PBMP_FILE_HEADER;
#pragma pack()

const BYTE MASK_TABLE_1[] = {0xff, 0x80, 0xa0, 0xe0, 0xf0, 0xf8, 0xfa, 0xfe};
const BYTE COUNT_TABLE_1[] = {
				0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
				1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
				1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
				1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
				2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
				3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
				3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
				4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
							};

#define ALLOCATE_BUFFER_SIZE 1024*1024

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ICBIStream3
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Porovnavacia funkcie pre AREA2
// .....................................................................................................................
int AREA2_compare3( const void *arg1, const void *arg2 )
{
	PAREA2 P1 = *(PAREA2*)arg1;
	PAREA2 P2 = *(PAREA2*)arg2;
	if (P1->W != P2->W) return P1->W > P2->W ? 1 : -1;
	if (P1->H != P2->H) return P1->H > P2->H ? 1 : -1;
	return 0;
}
// .....................................................................................................................

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
ICBIStream3::ICBIStream3(void) {
	FCompressionMode = 3;
	AreasData = NULL;
	BaseAreasData = NULL;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
ICBIStream3::~ICBIStream3(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream3::Create(CONST_PCHAR pFileName) {
	INT32 R = ICBIStream::Create(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Uzatvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream3::Close(void) {
	if (FFile == NULL) return 0; // Uzatvoreny?
	
	if (AreasData != NULL) delete AreasData;
	if (BaseAreasData != NULL) delete BaseAreasData;
	AreasData = NULL;
	BaseAreasData = NULL;
	
	if (FReadMode) return ICBIStream::Close(); // Volam predchodcu

	// Komprimacia dat
	IBitStream *BS = new IBitStream();

	PAREA2 *AList1 = new PAREA2[FAreas.Count];
	for (INT32 i = 0; i < FAreas.Count; i++) {
		AList1[i] = (PAREA2)FAreas.Items[i];
	}

	INT32 BaseCount = 0;
	PAREA2 *AList2 = new PAREA2[FAreas.Count];

	for (INT32 i = 0; i < FAreas.Count; i++) {
		INT32 Idx = -1;
		for (INT32 j = 1; j < 65535; j++) {
			INT32 I = i - j;
			if (I < 0) break;
			if (ICBIStream2::CompareAREA(AList1[i], AList1[I]) == 0) {
				Idx = AList1[I]->BaseIndex;
				break;
			}
		}
		if (Idx == -1) {
			AList1[i]->BaseIndex = BaseCount;
			AList1[i]->BaseItemCount = 1;
			AList2[BaseCount++] = AList1[i];
		} else {
			AList1[i]->BaseIndex = Idx;
			AList1[Idx]->BaseItemCount++;
		}
	}

	PAREA2 *AList3 = new PAREA2[BaseCount];
	memcpy(AList3, AList2, BaseCount * sizeof(PAREA2));

	qsort(AList2, BaseCount, sizeof(PAREA2), AREA2_compare3);
	for (INT32 i = 0; i < BaseCount; i++) {
		AList2[i]->BaseIndex2 = i;
	}
	for (INT32 i = 0; i < FAreas.Count; i++) {
		AList1[i]->BaseIndex = AList3[AList1[i]->BaseIndex]->BaseIndex2;
	}

	BS->AddDWORD(FAreas.Count);
	BS->AddDWORD(BaseCount);

	BYTE PrevW = 0, PrevH = 0;
	for (INT32 i = 0; i < BaseCount; i++) {
		BS->AddBYTE(AList2[i]->W - PrevW);
		if (AList2[i]->W != PrevW) PrevH = 0;
		PrevW = AList2[i]->W;
		BS->AddBYTE(AList2[i]->H - PrevH);
		PrevH = AList2[i]->H;
	}

	for (int x = 0; x < MAX_AREA2_WIDTH_B; x++) {
		for (INT32 i = 0; i < BaseCount; i++) { // Ulozim hlbky
			if (x * 8 < AList2[i]->W) {
				if ((AList2[i]->H > 1) && (AList2[i]->W > 1)) {
					for (INT32 y = 0; y < AList2[i]->H; y++) {
						BS->AddBYTE(AList2[i]->RawData[y * MAX_AREA2_WIDTH_B + x]);
					}
				}
			}
		}
	}

	if (BaseCount < 256) {
		for (INT32 i = 0; i < FAreas.Count; i++) {
			BS->AddBYTE(AList1[i]->BaseIndex);
		}
	} else {
		if (BaseCount < 65536) {
			for (INT32 i = 0; i < FAreas.Count; i++) {
				BS->AddWORD(AList1[i]->BaseIndex);
			}
		} else {
			for (INT32 i = 0; i < FAreas.Count; i++) {
				BS->AddDWORD(AList1[i]->BaseIndex);
			}
		}
	}

	UINT32 Old = 0;
	for (INT32 i = 0; i < FAreas.Count; i++) {
		BS->AddDWORD(AList1[i]->AbsPos - Old);
		Old = AList1[i]->AbsPos + AList1[i]->W;
	}

	delete AList1;
	delete AList2;
	delete AList3;

	// Uvolnim pamat
	for (INT32 i = 0; i < FAreas.Count; i++) {
		delete (PAREA2)FAreas.Items[i];
	}

	UINT32 U32 = BS->GetCurrentLength();
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem nekomprimovanu dlzku
		delete BS;
		return CBI_ERROR_WRITE_FILE;
	}

	IBitStream *BComp = new IBitStream();
	U32 = CompressBitStream(BS, BComp); // Komprimujem
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem komprimovanu dlzku
		delete BS;
		delete BComp;
		return CBI_ERROR_WRITE_FILE;
	}

	bool B = fwrite(BComp->Data, 1, U32, (FILE*)FFile) == U32; // Zapisem komprimovane data

	delete BS;
	delete BComp;


	UINT64 HeaderOffset = _ftelli64((FILE*)FFile); // Ziskam offset
	CBI_STREAM_HEADER StreamH;
	StreamH.COMPRESSION = FCompressionMode;
	StreamH.DCREATE = 0;
	StreamH.PAGES_COUNT = Pages.Count;

	StreamH.HEIGHT = StreamH.WIDTH = 0; StreamH.ORIGINAL_SIZE = 0;
	StreamH.COMPRESSED_SIZE = U32;
	for (INT32 i = 0; i < Pages.Count; i++) {
		PCBI_PAGE_HEADER P = (PCBI_PAGE_HEADER)Pages.Items[i];
		StreamH.ORIGINAL_SIZE += P->ORIGINAL_SIZE;
		StreamH.COMPRESSED_SIZE += P->DATA_SIZE;
		if (StreamH.HEIGHT < P->HEIGHT) StreamH.HEIGHT = P->HEIGHT;
		if (StreamH.WIDTH < P->WIDTH) StreamH.WIDTH = P->WIDTH;
	}

	if (fwrite(&StreamH, sizeof(CBI_STREAM_HEADER), 1, (FILE*)FFile) != 1) {
		fclose((FILE*)FFile);
		::DeleteFile(FFileName); // Zrusim subor
		return CBI_ERROR_WRITE_FILE;
	}

	for (INT32 i = 0; i < Pages.Count; i++) {
		PCBI_PAGE_HEADER P = (PCBI_PAGE_HEADER)Pages.Items[i];
		if (fwrite(P, sizeof(CBI_PAGE_HEADER), 1, (FILE*)FFile) != 1) {
			fclose((FILE*)FFile);
			::DeleteFile(FFileName); // Zrusim subor
			return CBI_ERROR_WRITE_FILE;
		}
	}

	if (fwrite(&HeaderOffset, sizeof(UINT64), 1, (FILE*)FFile) != 1) {
		fclose((FILE*)FFile);
		::DeleteFile(FFileName); // Zrusim subor
		return CBI_ERROR_WRITE_FILE;
	}

	fclose((FILE*)FFile); // Uzatvorim subor
	FFile = NULL; // Inicializacia
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie stranky
// .....................................................................................................................
INT32 __fastcall ICBIStream3::AddPage(IBitmap2 *pImage) {
	CBI_PAGE_HEADER *pPageInfo = new CBI_PAGE_HEADER();

	pPageInfo->DATA_OFFSET = Pages.Count == 0 ? 0 : ((PCBI_PAGE_HEADER)Pages.Items[Pages.Count - 1])->DATA_OFFSET + ((PCBI_PAGE_HEADER)Pages.Items[Pages.Count - 1])->WIDTH * ((PCBI_PAGE_HEADER)Pages.Items[Pages.Count - 1])->HEIGHT;

	pPageInfo->DPI_X = 0; pPageInfo->DPI_Y = 0;
	pPageInfo->COLOR_BACK = pImage->Pallete[0]; pPageInfo->COLOR_FORE = pImage->Pallete[1];
	pPageInfo->WIDTH = pImage->Width; pPageInfo->HEIGHT = pImage->Height;
	pPageInfo->ORIGINAL_SIZE = pImage->RawDataSize;

	IBitmap2 *WorkCopy = pImage->Clone(); // Vytvorim kopiu
	SEARCH_AREA2 SA; SA.Image = WorkCopy;

	INT32 FindX = -1, FindY = -1;
	while(WorkCopy->FindNextForegroundPixel(FindX, FindY)) {
		ICBIStream2::InitStructure(FindX, FindY, &SA);
		ICBIStream2::InspectArea(&SA);
		PAREA2 Area = new AREA2;
		ICBIStream2::CreateAreaFromSearchStruct(pImage, &SA, Area);
		Area->AbsPos += (INT32)pPageInfo->DATA_OFFSET;
		FAreas.Add(Area);
		if (FAreas.Count == 1921) {
			INT32 S = 01;
		}
	}

	delete WorkCopy;
	Pages.Add(pPageInfo);

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Otvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream3::Open(CONST_PCHAR pFileName) {

	INT32 R = ICBIStream::Open(pFileName); // Volam predchodcu
	if (R != 0) return R;

	if (_fseeki64((FILE*)FFile, sizeof(CBI_FILE_HEADER), SEEK_SET) != 0) return CBI_ERROR_READ_FILE; // Nastavim sa na zaciatok dat

	UINT32 U1, U2;
	if (fread(&U1, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam nekomprimovanu dlzku
	if (fread(&U2, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam komprimovanu dlzku

	IBitStream *BComp = new IBitStream();
	BComp->Allocate(U2);
	if (fread(BComp->Data, 1, U2, (FILE*)FFile) != U2) { // Nacitam komprimovane data do streamu
		delete BComp;
		return CBI_ERROR_READ_FILE;
	}

	IBitStream *BS = new IBitStream(); // Nacitam bitstream
	BS->Allocate(U1);
	if (DecompressBitStream(BComp, U1, BS) != U1) {
		delete BComp;
		delete BS;
		return CBI_ERROR_INVALID_STRUCTURE;
	}

	delete BComp; // Zrusim nepotrebny stream
	BS->Rewind(); // Nastavim sa na zaciatok

	AreasCount = BS->GetDWORD();
	BaseCount = BS->GetDWORD();

	AreasData = new MINIAREA2[AreasCount];
	BaseAreasData = new AREA2[BaseCount];

	BYTE PrevW = 0, PrevH = 0;
	for (INT32 i = 0; i < BaseCount; i++) {
		BaseAreasData[i].W = BS->GetBYTE() + PrevW;
		if (PrevW != BaseAreasData[i].W) PrevH = 0;
		PrevW = BaseAreasData[i].W;
		BaseAreasData[i].H = BS->GetBYTE() + PrevH;
		PrevH = BaseAreasData[i].H;
		if ((BaseAreasData[i].W > 1) && (BaseAreasData[i].H > 1)) memset(BaseAreasData[i].RawData, 0, DATA_AREA2_LENGTH);
		else memset(BaseAreasData[i].RawData, 0xff, DATA_AREA2_LENGTH);
	}

	for (int x = 0; x < MAX_AREA2_WIDTH_B; x++) {
		for (INT32 i = 0; i < BaseCount; i++) { // Ulozim hlbky
			if (x * 8 < BaseAreasData[i].W) {
				if ((BaseAreasData[i].H > 1) && (BaseAreasData[i].W > 1)) {
						for (INT32 y = 0; y < BaseAreasData[i].H; y++) {
							BaseAreasData[i].RawData[y * MAX_AREA2_WIDTH_B + x] = BS->GetBYTE();
						}
				}
			}
		}
	}

	if (BaseCount < 256) {
		for (INT32 i = 0; i < AreasCount; i++) {
			AreasData[i].BaseIndex = BS->GetBYTE();
		}
	} else {
		if (BaseCount < 65536) {
			for (INT32 i = 0; i < AreasCount; i++) {
				AreasData[i].BaseIndex = BS->GetWORD();
			}
		} else {
			for (INT32 i = 0; i < AreasCount; i++) {
				AreasData[i].BaseIndex = BS->GetDWORD();
			}
		}
	}

	UINT32 Old = 0;
	for (INT32 i = 0; i < AreasCount; i++) {
		AreasData[i].AbsPos = BS->GetDWORD() + Old;
		Old = AreasData[i].AbsPos + BaseAreasData[AreasData[i].BaseIndex].W;
	}

	delete BS;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie stranky ako obrazku
// .....................................................................................................................
INT32 __fastcall ICBIStream3::GetPageImage(INT32 pIndex, IBitmap2 *Result) {
	if ((pIndex < 0) || (pIndex >= Pages.Count)) return CBI_ERROR_PAGE_OUT_OF_RANGE;
	PCBI_PAGE_HEADER PageInfo = (PCBI_PAGE_HEADER)Pages.Items[pIndex];

	Result->Create(PageInfo->WIDTH, PageInfo->HEIGHT); // Vytvorim stranku

	INT32 OffsetMax = (INT32)(PageInfo->DATA_OFFSET + PageInfo->HEIGHT * PageInfo->WIDTH);
	for (INT32 i = 0; i < AreasCount; i++) {
		if (AreasData[i].AbsPos < PageInfo->DATA_OFFSET) continue;
		if (AreasData[i].AbsPos >= OffsetMax) break;

		INT32 U = AreasData[i].AbsPos - (UINT32)PageInfo->DATA_OFFSET;
		AreasData[i].Y = U / Result->Width;
		AreasData[i].X = U - (AreasData[i].Y * Result->Width);

		ICBIStream2::DrawArea(Result, BaseAreasData + AreasData[i].BaseIndex, AreasData[i].X, AreasData[i].Y);
	}

	return 0;
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ICBIStream2
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Porovnavacia funkcie pre AREA2
// .....................................................................................................................
int AREA2_compare( const void *arg1, const void *arg2 )
{
	PAREA2 P1 = *(PAREA2*)arg1;
	PAREA2 P2 = *(PAREA2*)arg2;
	if (P1->W != P2->W) return P1->W > P2->W ? 1 : -1;
	if (P1->H != P2->H) return P1->H > P2->H ? 1 : -1;
	return 0;
}
// .....................................................................................................................
  
// .....................................................................................................................
// Inicializacia struktury SEARCH_AREA
// .....................................................................................................................
void __fastcall ICBIStream2::InitStructure(INT32 pX, INT32 pY, PSEARCH_AREA2 pValue) {
	pValue->Height = pValue->Width = 1;
	pValue->MinX = pValue->MaxX = pValue->X = pX;
	pValue->MinY = pValue->MaxY = pValue->Y = pY;
	pValue->MaskStartX = pX - MAX_AREA2_WIDTH, pValue->MaskStartY = pY;
	memset(pValue->UsedMask, 0, 2 * MAX_AREA2_WIDTH * MAX_AREA2_HEIGHT);
}
// .....................................................................................................................
// .....................................................................................................................
// Presetrenie oblasti a vygenerovanie okrajov a vyplne
// .....................................................................................................................
void __fastcall ICBIStream2::InspectArea(PSEARCH_AREA2 pArea) {
	INT32 X = pArea->X; INT32 Y = pArea->Y;
	if (pArea->MinX > X) {
		if (pArea->Width == MAX_AREA2_WIDTH) return;
		pArea->MinX = X; pArea->Width++;
	}
	if (pArea->MaxX < X) {
		if (pArea->Width == MAX_AREA2_WIDTH) return;
		pArea->MaxX = X; pArea->Width++;
	}
	if (pArea->MinY > Y) {
		if (pArea->Height == MAX_AREA2_HEIGHT) return;
		pArea->MinY = Y; pArea->Height++;
	}
	if (pArea->MaxY < Y) {
		if (pArea->Height == MAX_AREA2_HEIGHT) return;
		pArea->MaxY = Y; pArea->Height++;
	}
	pArea->Image->SetPixel(X, Y, false); // Zrusim zaradeny pixel
	pArea->UsedMask[((Y - pArea->MaskStartY) * (2 * MAX_AREA2_WIDTH)) + (X - pArea->MaskStartX)] = 1;

	if (pArea->Image->GetPixel(X, Y - 1)) {
		pArea->X = X; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X - 1, Y)) {
		pArea->X = X - 1; pArea->Y = Y;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y)) {
		pArea->X = X + 1; pArea->Y = Y;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X, Y + 1)) {
		pArea->X = X; pArea->Y = Y + 1;
		InspectArea(pArea);
	}

	if (pArea->Image->GetPixel(X - 1, Y - 1)) {
		pArea->X = X - 1; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y - 1)) {
		pArea->X = X + 1; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X - 1, Y + 1)) {
		pArea->X = X - 1; pArea->Y = Y + 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y + 1)) {
		pArea->X = X + 1; pArea->Y = Y + 1;
		InspectArea(pArea);
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
ICBIStream2::ICBIStream2(void) {
	FCompressionMode = 2;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
ICBIStream2::~ICBIStream2(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream2::Create(CONST_PCHAR pFileName) {
	INT32 R = ICBIStream::Create(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Uzatvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream2::Close(void) {

	INT32 R = ICBIStream::Close(); // Volam predchodcu
	return R;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie stranky
// .....................................................................................................................
INT32 __fastcall ICBIStream2::AddPage(IBitmap2 *pImage) {
	CBI_PAGE_HEADER *pPageInfo = new CBI_PAGE_HEADER();

	pPageInfo->DPI_X = 0; pPageInfo->DPI_Y = 0;
	pPageInfo->COLOR_BACK = pImage->Pallete[0]; pPageInfo->COLOR_FORE = pImage->Pallete[1];
	pPageInfo->WIDTH = pImage->Width; pPageInfo->HEIGHT = pImage->Height;
	pPageInfo->ORIGINAL_SIZE = pImage->RawDataSize;
	pPageInfo->DATA_OFFSET = _ftelli64((FILE*)FFile);

	IBitmap2 *WorkCopy = pImage->Clone(); // Vytvorim kopiu
	SEARCH_AREA2 SA; SA.Image = WorkCopy;
	IBitStream *BS = new IBitStream();

	INT32 MaxAreasCount = 1024; INT32 ADataCounter = 0;
	PAREA2 AreasDataM[32]; for (INT32 i = 0; i < 32; i++) AreasDataM[i] = NULL;

	AreasDataM[0] = new AREA2[MaxAreasCount];
	PAREA2 AreasData = AreasDataM[0];

	INT32 AreasCount = 0, AreasCount1 = 0;
	INT32 FindX = -1, FindY = -1;
	while(WorkCopy->FindNextForegroundPixel(FindX, FindY)) {
		ICBIStream2::InitStructure(FindX, FindY, &SA);
		ICBIStream2::InspectArea(&SA);
		ICBIStream2::CreateAreaFromSearchStruct(pImage, &SA, &AreasData[AreasCount1]);
		AreasCount++; AreasCount1++;
		if (AreasCount1 == MaxAreasCount) { // Budem vytvarat dalsie pole?
			ADataCounter++;
			AreasDataM[ADataCounter] = new AREA2[MaxAreasCount];
			AreasData = AreasDataM[ADataCounter];
			AreasCount1 = 0;
		}
	}

	delete WorkCopy;

	PAREA2 *AList1 = new PAREA2[AreasCount];
	AreasCount1 = 0;
	for (INT32 i = 0; i <= ADataCounter; i++) {
		AreasData = AreasDataM[i];
		if (AreasCount == AreasCount1) break;
		for (INT32 j = 0; j < MaxAreasCount; j++) {
			if (AreasCount == AreasCount1) break;
			AList1[AreasCount1] = AreasData + j;
			AList1[AreasCount1]->NormalIndex = AreasCount1;
			AreasCount1++;
		}
	}

	INT32 BaseCount = 0;
	PAREA2 *AList2 = new PAREA2[AreasCount1];

	for (INT32 i = 0; i < AreasCount1; i++) {
		INT32 Idx = -1;
		for (INT32 j = 1; j < 65535; j++) {
			INT32 I = i - j;
			if (I < 0) break;
			if (ICBIStream2::CompareAREA(AList1[i], AList1[I]) == 0) {
				Idx = AList1[I]->BaseIndex;
				break;
			}
		}
		if (Idx == -1) {
			AList1[i]->BaseIndex = BaseCount;
			AList1[i]->BaseItemCount = 1;
			AList2[BaseCount++] = AList1[i];
		} else {
			AList1[i]->BaseIndex = Idx;
			AList1[Idx]->BaseItemCount++;
		}
	}

	PAREA2 *AList3 = new PAREA2[BaseCount];
	memcpy(AList3, AList2, BaseCount * sizeof(PAREA2));

	qsort(AList2, BaseCount, sizeof(PAREA2), AREA2_compare);
	for (INT32 i = 0; i < BaseCount; i++) {
		AList2[i]->BaseIndex2 = i;
	}
	for (INT32 i = 0; i < AreasCount1; i++) {
		AList1[i]->BaseIndex = AList3[AList1[i]->BaseIndex]->BaseIndex2;
	}

	BS->AddDWORD(AreasCount1);
	BS->AddDWORD(BaseCount);

	for (INT32 i = 0; i < BaseCount; i++) {
		BS->AddBYTE(AList2[i]->W - 1);
		BS->AddBYTE(AList2[i]->H - 1);
	}
	for (int x = 0; x < MAX_AREA2_WIDTH_B; x++) {
		for (INT32 i = 0; i < BaseCount; i++) { // Ulozim hlbky
			if ((AList2[i]->H > 1) && (AList2[i]->W > 1)) {
				for (INT32 y = 0; y < AList2[i]->H; y++) {
					BS->AddBYTE(AList2[i]->RawData[y * MAX_AREA2_WIDTH_B + x]);
				}
			}
		}
	}

	if (BaseCount < 256) {
		for (INT32 i = 0; i < AreasCount1; i++) {
			BS->AddBYTE(AList1[i]->BaseIndex);
		}
	} else {
		if (BaseCount < 65536) {
			for (INT32 i = 0; i < AreasCount1; i++) {
				BS->AddWORD(AList1[i]->BaseIndex);
			}
		} else {
			for (INT32 i = 0; i < AreasCount1; i++) {
				BS->AddDWORD(AList1[i]->BaseIndex);
			}
		}
	}

	UINT32 Old = 0;
	for (INT32 i = 0; i < AreasCount1; i++) {
		BS->AddDWORD(AList1[i]->AbsPos - Old);
		Old = AList1[i]->AbsPos;
	}

	// Uvolnim pamat
	for (INT32 i = 0; i <= ADataCounter; i++) {
		if (AreasDataM[i] == NULL) break;
		delete AreasDataM[i];
	}

	delete AList1;
	delete AList2;
	delete AList3;

	UINT32 U32 = BS->GetCurrentLength();
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem nekomprimovanu dlzku
		delete BS;
		return CBI_ERROR_WRITE_FILE;
	}

	IBitStream *BComp = new IBitStream();
	U32 = CompressBitStream(BS, BComp); // Komprimujem
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem komprimovanu dlzku
		delete BS;
		delete BComp;
		return CBI_ERROR_WRITE_FILE;
	}

	bool B = fwrite(BComp->Data, 1, U32, (FILE*)FFile) == U32; // Zapisem komprimovane data
	pPageInfo->DATA_SIZE = BComp->GetCurrentLength() + 2 * 4; // Ulozim raw dlzku dat

	delete BS;
	delete BComp;

	if (B) Pages.Add(pPageInfo); else delete pPageInfo;
	return B ? 0 : CBI_ERROR_WRITE_FILE;
}
// .....................................................................................................................
// .....................................................................................................................
// Otvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream2::Open(CONST_PCHAR pFileName) {

	INT32 R = ICBIStream::Open(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie stranky ako obrazku
// .....................................................................................................................
INT32 __fastcall ICBIStream2::GetPageImage(INT32 pIndex, IBitmap2 *Result) {
	if ((pIndex < 0) || (pIndex >= Pages.Count)) return CBI_ERROR_PAGE_OUT_OF_RANGE;
	PCBI_PAGE_HEADER PageInfo = (PCBI_PAGE_HEADER)Pages.Items[pIndex];

	if (_fseeki64((FILE*)FFile, PageInfo->DATA_OFFSET, SEEK_SET) != 0) return CBI_ERROR_READ_FILE; // Nastavim sa na zaciatok stranky

	Result->Create(PageInfo->WIDTH, PageInfo->HEIGHT); // Vytvorim stranku

	UINT32 U1, U2;
	if (fread(&U1, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam nekomprimovanu dlzku
	if (fread(&U2, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam komprimovanu dlzku

	IBitStream *BComp = new IBitStream();
	BComp->Allocate(U2);
	if (fread(BComp->Data, 1, U2, (FILE*)FFile) != U2) { // Nacitam komprimovane data do streamu
		delete BComp;
		return CBI_ERROR_READ_FILE;
	}

	IBitStream *BS = new IBitStream(); // Nacitam bitstream
	BS->Allocate(U1);
	if (DecompressBitStream(BComp, U1, BS) != U1) {
		delete BComp;
		delete BS;
		return CBI_ERROR_INVALID_STRUCTURE;
	}

	delete BComp; // Zrusim nepotrebny stream
	BS->Rewind(); // Nastavim sa na zaciatok

	INT32 AreasCount = BS->GetDWORD();
	INT32 BaseCount = BS->GetDWORD();

	PAREA2 AreasData = new AREA2[AreasCount];
	PAREA2 BaseAreasData = new AREA2[BaseCount];

	for (INT32 i = 0; i < BaseCount; i++) {
		BaseAreasData[i].W = BS->GetBYTE() + 1;
		BaseAreasData[i].H = BS->GetBYTE() + 1;
		memset(BaseAreasData[i].RawData, 0, MAX_AREA2_WIDTH_B);
	}

	for (int x = 0; x < MAX_AREA2_WIDTH_B; x++) {
		for (INT32 i = 0; i < BaseCount; i++) { // Ulozim hlbky
			if ((BaseAreasData[i].H > 1) && (BaseAreasData[i].W > 1)) {
				for (INT32 y = 0; y < BaseAreasData[i].H; y++) {
					BaseAreasData[i].RawData[y * MAX_AREA2_WIDTH_B + x] = BS->GetBYTE();
				}
			}
		}
	}

	if (BaseCount < 256) {
		for (INT32 i = 0; i < AreasCount; i++) {
			AreasData[i].BaseIndex = BS->GetBYTE();
		}
	} else {
		if (BaseCount < 65536) {
			for (INT32 i = 0; i < AreasCount; i++) {
				AreasData[i].BaseIndex = BS->GetWORD();
			}
		} else {
			for (INT32 i = 0; i < AreasCount; i++) {
				AreasData[i].BaseIndex = BS->GetDWORD();
			}
		}
	}

	UINT32 Old = 0;
	for (INT32 i = 0; i < AreasCount; i++) {
		AreasData[i].AbsPos = BS->GetDWORD() + Old;
		AreasData[i].Y = AreasData[i].AbsPos / Result->Width;
		AreasData[i].X = AreasData[i].AbsPos - (AreasData[i].Y * Result->Width);
		Old = AreasData[i].AbsPos;
	}

	for (INT32 i = 0; i < AreasCount; i++) {
		ICBIStream2::DrawArea(Result, BaseAreasData + AreasData[i].BaseIndex, AreasData[i].X, AreasData[i].Y);
	}

	delete BS; // Uvolnim pamat
	delete BaseAreasData;
	delete AreasData;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Nastavenie pixela
// .....................................................................................................................
void __fastcall ICBIStream2::SetPixel(PAREA2 pArea, INT32 pX, INT32 pY) {
	BYTE M = 0x80; M >>= pX & 0x7;
	pArea->RawData[pY * MAX_AREA2_WIDTH_B + (pX >> 3)] |= M;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie hodnoty pixela
// .....................................................................................................................
bool __fastcall ICBIStream2::GetPixel(PAREA2 pArea, INT32 pX, INT32 pY) {
	BYTE M = 0x80; M >>= pX & 0x7;
	return (pArea->RawData[pY * MAX_AREA2_WIDTH_B + (pX >> 3)] & M) != 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Porovnanie AREA
// .....................................................................................................................
INT32 __fastcall ICBIStream2::CompareAREA(PAREA2 P1, PAREA2 P2) {
	return memcmp(P1->RawData, P2->RawData, DATA_AREA2_LENGTH);
}
// .....................................................................................................................
// .....................................................................................................................
// Vykreslenie oblasti
// .....................................................................................................................
void __fastcall ICBIStream2::DrawArea(IBitmap2 *pImage, PAREA2 pArea, INT32 pX, INT32 pY) {
	for (INT32 y = 0; y < pArea->H; y++) {
		for (INT32 x = 0; x < pArea->W; x++) {
			bool B = GetPixel(pArea, x, y);
			if (B) pImage->SetPixel(pX + x, pY + y, true);
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie AREA2 z originalneho obrazku a SEARCH_AREA
// .....................................................................................................................
void __fastcall ICBIStream2::CreateAreaFromSearchStruct(IBitmap2 *pImage, PSEARCH_AREA2 pSearchArea, PAREA2 Result) {
	Result->X = pSearchArea->MinX; Result->Y = pSearchArea->MinY;
	Result->W = pSearchArea->Width; Result->H = pSearchArea->Height;
	Result->AbsPos = Result->Y * pImage->Width + Result->X;
	Result->NormalIndex = Result->BaseIndex = 0; Result->Count1 = 0;
	memset(Result->RawData, 0, DATA_AREA2_LENGTH);

	for (INT32 y = 0; y < pSearchArea->Height; y++) {
		for (INT32 x = 0; x < pSearchArea->Width; x++) {
			INT32 X = pSearchArea->MinX + x;
			INT32 Y = pSearchArea->MinY + y;
			if (pSearchArea->UsedMask[(Y - pSearchArea->MaskStartY) * 2 * MAX_AREA2_WIDTH + (X - pSearchArea->MaskStartX)] == 1) {
				Result->Count1++;
				SetPixel(Result, x, y);
			}
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Ulozenie area
// .....................................................................................................................
void __fastcall ICBIStream2::SaveAREAData(IBitStream *pStream, PAREA2 pArea) {
	if ((pArea->H > 1) && (pArea->W > 1)) {
		for (INT32 y = 0; y < pArea->H; y++) {
			for (INT32 x = 0; x < pArea->W; x++) {
				pStream->AddBIT(GetPixel(pArea, x, y));
			}
			pStream->AlignWriteToBYTE(false);
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Ulozenie area
// .....................................................................................................................
void __fastcall ICBIStream2::SaveAREAData(IBitStream *pStream, PAREA2 pArea, INT32 pCol) {
	if ((pArea->H > 1) && (pArea->W > 1)) {
		for (INT32 y = 0; y < pArea->H; y++) {
			for (INT32 x = pCol * 8; x < (pCol + 1) * 8; x++) {
				if (x >= pArea->W) continue;
				pStream->AddBIT(GetPixel(pArea, x, y));
			}
			pStream->AlignWriteToBYTE(false);
		}
	}
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// IList
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
IList::IList(void) {
	Items = NULL; Count = Capacity = 0; // Inicializacia
	Items = new PVOID[65536]; Capacity = 65536;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
IList::~IList(void) {
	Clear(); // Uvolnim z pamate
}
// .....................................................................................................................
// .....................................................................................................................
// Uvolnenie zoznamu z pamate
// .....................................................................................................................
void __fastcall IList::Clear(void) {
	if (Items != NULL) delete Items; // Zrusim pamat
	Items = NULL; Count = Capacity = 0; // Inicializacia
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie hodnoty
// .....................................................................................................................
INT32 __fastcall IList::Add(PVOID pValue) {
	INT32 C = Count;
	Reallocate(Count + 1, true); // Alokujem pamat
	Items[Count++] = pValue;
	return C;
}
// .....................................................................................................................
// .....................................................................................................................
// Realokacia pamate podla potreby
// .....................................................................................................................
void __fastcall IList::Reallocate(INT32 pCapacity, bool pCopy) {
	if (Capacity >= pCapacity) return;
	Capacity = ALIGN_2048(pCapacity); // Nova kapacita
	PVOID *NW = new PVOID[Capacity]; // Alokujem pamat
	if (Items != NULL) {
		if ((pCopy) && (Count > 0)) memcpy(NW, Items, Count * sizeof(PVOID)); // Skopirujem ak je treba
		delete Items; // Zrusim staru pamat
	}
	Items = NW;
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ICBIStream
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Vytvorenie objektu streamu podla typu kompresie
// .....................................................................................................................
ICBIStream* __fastcall ICBIStream::CreateCBIStream(INT32 pCompressionMode) {
	switch(pCompressionMode) {
		case 3: return new ICBIStream3();
		case 2: return new ICBIStream2();
		case 1: return new ICBIStream1();
		default: return new ICBIStream0();
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
ICBIStream::ICBIStream(void) {
	FFile = NULL;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
ICBIStream::~ICBIStream(void) {
	ClearPages(); // Uvolnim stranky
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream::Create(CONST_PCHAR pFileName) {
	Close(); // Uzatvorim 

	FFile = (FILE*)fopen(pFileName, "wb+");
	if (FFile == NULL) return CBI_ERROR_CREATE_FILE;

	CBI_FILE_HEADER FH; 
	FH.SIGNATURE[0] = 'C'; FH.SIGNATURE[1] = 'B'; FH.SIGNATURE[2] = 'I'; FH.SIGNATURE[3] = '0';
	if (fwrite(&FH, sizeof(CBI_FILE_HEADER), 1, (FILE*)FFile) != 1) {
		fclose((FILE*)FFile);
		::DeleteFile(pFileName); // Zrusim subor
		return CBI_ERROR_WRITE_FILE;
	}

	strcpy(FFileName, pFileName);
	FReadMode = false;
	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Uzatvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream::Close(void) {
	if (FFile == NULL) return 0; // Uzatvoreny?

	if (FReadMode) {
		ClearPages();
		fclose((FILE*)FFile);
		FFile = NULL; // Inicializacia
		return 0;
	}

	UINT64 HeaderOffset = _ftelli64((FILE*)FFile); // Ziskam offset
	CBI_STREAM_HEADER StreamH;
	StreamH.COMPRESSION = FCompressionMode;
	StreamH.DCREATE = 0;
	StreamH.PAGES_COUNT = Pages.Count;

	StreamH.HEIGHT = StreamH.WIDTH = 0; StreamH.ORIGINAL_SIZE = 0;
	StreamH.COMPRESSED_SIZE = 0;
	for (INT32 i = 0; i < Pages.Count; i++) {
		PCBI_PAGE_HEADER P = (PCBI_PAGE_HEADER)Pages.Items[i];
		StreamH.ORIGINAL_SIZE += P->ORIGINAL_SIZE;
		StreamH.COMPRESSED_SIZE += P->DATA_SIZE;
		if (StreamH.HEIGHT < P->HEIGHT) StreamH.HEIGHT = P->HEIGHT;
		if (StreamH.WIDTH < P->WIDTH) StreamH.WIDTH = P->WIDTH;
	}

	if (fwrite(&StreamH, sizeof(CBI_STREAM_HEADER), 1, (FILE*)FFile) != 1) {
		fclose((FILE*)FFile);
		::DeleteFile(FFileName); // Zrusim subor
		return CBI_ERROR_WRITE_FILE;
	}

	for (INT32 i = 0; i < Pages.Count; i++) {
		PCBI_PAGE_HEADER P = (PCBI_PAGE_HEADER)Pages.Items[i];
		if (fwrite(P, sizeof(CBI_PAGE_HEADER), 1, (FILE*)FFile) != 1) {
			fclose((FILE*)FFile);
			::DeleteFile(FFileName); // Zrusim subor
			return CBI_ERROR_WRITE_FILE;
		}
	}

	if (fwrite(&HeaderOffset, sizeof(UINT64), 1, (FILE*)FFile) != 1) {
		fclose((FILE*)FFile);
		::DeleteFile(FFileName); // Zrusim subor
		return CBI_ERROR_WRITE_FILE;
	}

	fclose((FILE*)FFile); // Uzatvorim subor
	FFile = NULL; // Inicializacia
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Uvolnenie stranok
// .....................................................................................................................
void __fastcall ICBIStream::ClearPages(void) {
	for (INT32 i = 0; i < Pages.Count; i++) {
		delete (PCBI_PAGE_HEADER)Pages.Items[i];
	}
	Pages.Clear(); // Vymazem zoznam
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie stranky
// .....................................................................................................................
INT32 __fastcall ICBIStream::AddPage(IBitmap2 *pImage) {
	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie prislusneho objektu a otvorenie streamu
// .....................................................................................................................
ICBIStream* __fastcall ICBIStream::OpenCBIStream(CONST_PCHAR pFileName, INT32 &ErrorCode) {

	FILE *F = fopen(pFileName, "rb"); // Otvorim subor
	if (F == NULL) {
		ErrorCode = CBI_ERROR_OPEN_FILE;
		return NULL;
	}

	CBI_FILE_HEADER FH; 
	if (fread(&FH, sizeof(CBI_FILE_HEADER), 1, F) != 1) { // Nacitam signaturu
		fclose(F);
		ErrorCode = CBI_ERROR_READ_FILE;
		return NULL;
	}
	if ((FH.SIGNATURE[0] != 'C') || (FH.SIGNATURE[1] != 'B') || (FH.SIGNATURE[2] != 'I')) { // Kontrola hlavicky
		fclose(F);
		ErrorCode = CBI_ERROR_INVALID_STRUCTURE;
		return NULL;
	}
	switch(FH.SIGNATURE[3]) {
		case '0': 
			break;
		default: {
			fclose(F);
			ErrorCode = CBI_ERROR_INVALID_STRUCTURE;
			return NULL;
				 }
	}

	if (fseek(F, -8, SEEK_END) != 0) { // Nastavim sa na koniec a nacitam offset zaciatku hlavicky
		fclose(F);
		ErrorCode = CBI_ERROR_READ_FILE;
		return NULL;
	}

	UINT64 HeaderOffset;
	if (fread(&HeaderOffset, sizeof(UINT64), 1, F) != 1) {
		fclose(F);
		ErrorCode = CBI_ERROR_READ_FILE;
		return NULL;
	}

	if (_fseeki64(F, HeaderOffset, SEEK_SET) != 0) { // Nastavim sa na zaciatok hlavicky
		fclose(F);
		ErrorCode = CBI_ERROR_READ_FILE;
		return NULL;
	}

	CBI_STREAM_HEADER StreamH;
	if (fread(&StreamH, sizeof(CBI_STREAM_HEADER), 1, F) != 1) {
		fclose(F);
		ErrorCode = CBI_ERROR_READ_FILE;
		return NULL;
	}

	fclose(F); // Uzatvorim subor
	switch(StreamH.COMPRESSION) {
		case 0: {
			ICBIStream0 *Str = new ICBIStream0();
			ErrorCode = Str->Open(pFileName);
			if (ErrorCode == 0) return Str;
			delete Str;
			return NULL;
				}
		case 1: {
			ICBIStream1 *Str = new ICBIStream1();
			ErrorCode = Str->Open(pFileName);
			if (ErrorCode == 0) return Str;
			delete Str;
			return NULL;
				}
		case 2: {
			ICBIStream2 *Str = new ICBIStream2();
			ErrorCode = Str->Open(pFileName);
			if (ErrorCode == 0) return Str;
			delete Str;
			return NULL;
				}
		case 3: {
			ICBIStream3 *Str = new ICBIStream3();
			ErrorCode = Str->Open(pFileName);
			if (ErrorCode == 0) return Str;
			delete Str;
			return NULL;
				}
		default: {
			ErrorCode = CBI_ERROR_UNSUPPORTED_COMPRESSION;
			return NULL;
				 }
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Otvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream::Open(CONST_PCHAR pFileName) {
	Close(); // Uzatvorim subor
	FILE *F = fopen(pFileName, "rb"); // Otvorim subor
	if (F == NULL) return CBI_ERROR_OPEN_FILE;

	CBI_FILE_HEADER FH; 
	if (fread(&FH, sizeof(CBI_FILE_HEADER), 1, F) != 1) { // Nacitam signaturu
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}
	if ((FH.SIGNATURE[0] != 'C') || (FH.SIGNATURE[1] != 'B') || (FH.SIGNATURE[2] != 'I')) { // Kontrola hlavicky
		fclose(F);
		return CBI_ERROR_INVALID_STRUCTURE;
	}
	switch(FH.SIGNATURE[3]) {
		case '0': 
			break;
		default: {
			fclose(F);
			return CBI_ERROR_INVALID_STRUCTURE;
				 }
	}

	if (fseek(F, -8, SEEK_END) != 0) { // Nastavim sa na koniec a nacitam offset zaciatku hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	UINT64 HeaderOffset;
	if (fread(&HeaderOffset, sizeof(UINT64), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	if (_fseeki64(F, HeaderOffset, SEEK_SET) != 0) { // Nastavim sa na zaciatok hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	CBI_STREAM_HEADER StreamH;
	if (fread(&StreamH, sizeof(CBI_STREAM_HEADER), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	PCBI_PAGE_HEADER PageH;
	for (INT32 i = 0; i < (INT32)StreamH.PAGES_COUNT; i++) {
		PageH = new CBI_PAGE_HEADER;
		if (fread(PageH, sizeof(CBI_PAGE_HEADER), 1, F) != 1) {
			ClearPages();
			fclose(F);
			return CBI_ERROR_READ_FILE;
		}
		Pages.Add(PageH);
	}

	FFile = F;
	FReadMode = true;
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie stranky ako obrazku
// .....................................................................................................................
INT32 __fastcall ICBIStream::GetPageImage(INT32 pIndex, IBitmap2 *Result) {
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie informacii o streame
// .....................................................................................................................
INT32 __fastcall ICBIStream::ReadStreamInfo(CONST_PCHAR pFileName, PCBI_STREAM_INFO Result) {
	FILE *F = fopen(pFileName, "rb"); // Otvorim subor
	if (F == NULL) return CBI_ERROR_OPEN_FILE;

	CBI_FILE_HEADER FH; 
	if (fread(&FH, sizeof(CBI_FILE_HEADER), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}
	if ((FH.SIGNATURE[0] != 'C') || (FH.SIGNATURE[1] != 'B') || (FH.SIGNATURE[2] != 'I')) { // Kontrola hlavicky
		fclose(F);
		return CBI_ERROR_INVALID_STRUCTURE;
	}
	switch(FH.SIGNATURE[3]) {
		case '0': 
			break;
		default: {
			fclose(F);
			return CBI_ERROR_INVALID_STRUCTURE;
				 }
	}

	if (fseek(F, -8, SEEK_END) != 0) { // Nastavim sa na koniec a nacitam offset zaciatku hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	UINT64 HeaderOffset;
	if (fread(&HeaderOffset, sizeof(UINT64), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	if (_fseeki64(F, HeaderOffset, SEEK_SET) != 0) { // Nastavim sa na zaciatok hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	CBI_STREAM_HEADER StreamH;
	if (fread(&StreamH, sizeof(CBI_STREAM_HEADER), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	Result->COMPRESSED_SIZE = StreamH.COMPRESSED_SIZE; // Vyplnim vysledok
	Result->COMPRESSION = StreamH.COMPRESSION;
	Result->DCREATE = StreamH.DCREATE;
	Result->MAX_HEIGHT = StreamH.HEIGHT;
	Result->MAX_WIDTH = StreamH.WIDTH;
	Result->ORIGINAL_SIZE = StreamH.ORIGINAL_SIZE;
	Result->PAGES_COUNT = StreamH.PAGES_COUNT;

	fclose(F); // Uzatvorim subor
	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie info o strankach v cbi streame
// .....................................................................................................................
INT32 __fastcall ICBIStream::ReadPagesInfo(CONST_PCHAR pFileName, PCBI_PAGE_INFO Result) {
	FILE *F = fopen(pFileName, "rb"); // Otvorim subor
	if (F == NULL) return CBI_ERROR_OPEN_FILE;

	CBI_FILE_HEADER FH; 
	if (fread(&FH, sizeof(CBI_FILE_HEADER), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}
	if ((FH.SIGNATURE[0] != 'C') || (FH.SIGNATURE[1] != 'B') || (FH.SIGNATURE[2] != 'I')) { // Kontrola hlavicky
		fclose(F);
		return CBI_ERROR_INVALID_STRUCTURE;
	}
	switch(FH.SIGNATURE[3]) {
		case '0': 
			break;
		default: {
			fclose(F);
			return CBI_ERROR_INVALID_STRUCTURE;
				 }
	}

	if (fseek(F, -8, SEEK_END) != 0) { // Nastavim sa na koniec a nacitam offset zaciatku hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	UINT64 HeaderOffset;
	if (fread(&HeaderOffset, sizeof(UINT64), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	if (_fseeki64(F, HeaderOffset, SEEK_SET) != 0) { // Nastavim sa na zaciatok hlavicky
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	CBI_STREAM_HEADER StreamH;
	if (fread(&StreamH, sizeof(CBI_STREAM_HEADER), 1, F) != 1) {
		fclose(F);
		return CBI_ERROR_READ_FILE;
	}

	CBI_PAGE_HEADER PageH;
	for (INT32 i = 0; i < (INT32)StreamH.PAGES_COUNT; i++) {
		if (fread(&PageH, sizeof(CBI_PAGE_HEADER), 1, F) != 1) {
			fclose(F);
			return CBI_ERROR_READ_FILE;
		}
		Result[i].WIDTH = PageH.WIDTH;
		Result[i].HEIGHT = PageH.HEIGHT;
		Result[i].DPI_X = PageH.DPI_X;
		Result[i].DPI_Y = PageH.DPI_Y;
		Result[i].COLOR_BACK = PageH.COLOR_BACK;
		Result[i].COLOR_FORE = PageH.COLOR_FORE;
		Result[i].COMPRESSED_SIZE = PageH.DATA_SIZE;
		Result[i].ORIGINAL_SIZE = PageH.ORIGINAL_SIZE;
	}

	fclose(F); // Uzatvorim subor
	return 0; // Vsetko OK
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ICBIStream0
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
ICBIStream0::ICBIStream0(void) {
	FCompressionMode = 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
ICBIStream0::~ICBIStream0(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream0::Create(CONST_PCHAR pFileName) {
	INT32 R = ICBIStream::Create(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Uzatvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream0::Close(void) {

	INT32 R = ICBIStream::Close(); // Volam predchodcu
	return R;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie stranky
// .....................................................................................................................
INT32 __fastcall ICBIStream0::AddPage(IBitmap2 *pImage) {
	CBI_PAGE_HEADER *pPageInfo = new CBI_PAGE_HEADER();

	pPageInfo->DPI_X = 0; pPageInfo->DPI_Y = 0;
	pPageInfo->COLOR_BACK = pImage->Pallete[0]; pPageInfo->COLOR_FORE = pImage->Pallete[1];
	pPageInfo->WIDTH = pImage->Width; pPageInfo->HEIGHT = pImage->Height;
	pPageInfo->ORIGINAL_SIZE = pImage->RawDataSize;
	pPageInfo->DATA_SIZE = pImage->RawDataSize;
	pPageInfo->DATA_OFFSET = _ftelli64((FILE*)FFile);

	INT32 R = fwrite(pImage->RawData, 1, pImage->RawDataSize, (FILE*)FFile) != pImage->RawDataSize ? CBI_ERROR_WRITE_FILE : 0;
	if (R == 0) Pages.Add(pPageInfo); else delete pPageInfo;
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Otvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream0::Open(CONST_PCHAR pFileName) {

	INT32 R = ICBIStream::Open(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie stranky ako obrazku
// .....................................................................................................................
INT32 __fastcall ICBIStream0::GetPageImage(INT32 pIndex, IBitmap2 *Result) {
	if ((pIndex < 0) || (pIndex >= Pages.Count)) return CBI_ERROR_PAGE_OUT_OF_RANGE;
	PCBI_PAGE_HEADER PageInfo = (PCBI_PAGE_HEADER)Pages.Items[pIndex];

	Result->Create(PageInfo->WIDTH, PageInfo->HEIGHT); // Vytvorim stranku

	if (_fseeki64((FILE*)FFile, PageInfo->DATA_OFFSET, SEEK_SET) != 0) return CBI_ERROR_READ_FILE; // Nastavim sa na zaciatok stranky
	if (fread(Result->RawData, 1, Result->RawDataSize, (FILE*)FFile) != Result->RawDataSize) return CBI_ERROR_READ_FILE;
	return 0;
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ICBIStream1
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
ICBIStream1::ICBIStream1(void) {
	FCompressionMode = 1;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
ICBIStream1::~ICBIStream1(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream1::Create(CONST_PCHAR pFileName) {
	INT32 R = ICBIStream::Create(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0; // Vsetko OK
}
// .....................................................................................................................
// .....................................................................................................................
// Uzatvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream1::Close(void) {

	INT32 R = ICBIStream::Close(); // Volam predchodcu
	return R;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie stranky
// .....................................................................................................................
INT32 __fastcall ICBIStream1::AddPage(IBitmap2 *pImage) {
	CBI_PAGE_HEADER *pPageInfo = new CBI_PAGE_HEADER();

	pPageInfo->DPI_X = 0; pPageInfo->DPI_Y = 0;
	pPageInfo->COLOR_BACK = pImage->Pallete[0]; pPageInfo->COLOR_FORE = pImage->Pallete[1];
	pPageInfo->WIDTH = pImage->Width; pPageInfo->HEIGHT = pImage->Height;
	pPageInfo->ORIGINAL_SIZE = pImage->RawDataSize;
	pPageInfo->DATA_OFFSET = _ftelli64((FILE*)FFile);

	IBitmap2 *WorkCopy = pImage->Clone(); // Vytvorim kopiu
	SEARCH_AREA SA; SA.Image = WorkCopy;
	IBitStream *BS = new IBitStream();

	INT32 MaxAreasCount = 1024; INT32 ADataCounter = 0;
	PAREA AreasDataM[32]; for (INT32 i = 0; i < 32; i++) AreasDataM[i] = NULL;

	AreasDataM[0] = new AREA[MaxAreasCount];
	PAREA AreasData = AreasDataM[0];

	INT32 AreasCount = 0, AreasCount1 = 0;
	INT32 FindX = -1, FindY = -1;
	while(WorkCopy->FindNextForegroundPixel(FindX, FindY)) {
		InitStructure(FindX, FindY, &SA);
		InspectArea(&SA);
		CreateAreaFromSearchStruct(pImage, &SA, &AreasData[AreasCount1]);
		AreasCount++; AreasCount1++;
		if (AreasCount1 == MaxAreasCount) { // Budem vytvarat dalsie pole?
			ADataCounter++;
			AreasDataM[ADataCounter] = new AREA[MaxAreasCount];
			AreasData = AreasDataM[ADataCounter];
			AreasCount1 = 0;
		}
	}

	delete WorkCopy;

	PAREA *AList1 = new PAREA[AreasCount];
	AreasCount1 = 0;
	for (INT32 i = 0; i <= ADataCounter; i++) {
		AreasData = AreasDataM[i];
		if (AreasCount == AreasCount1) break;
		for (INT32 j = 0; j < MaxAreasCount; j++) {
			if (AreasCount == AreasCount1) break;
			AList1[AreasCount1] = AreasData + j;
			AreasCount1++;
		}
	}

	INT32 *DupIdx = new INT32[AreasCount]; memset(DupIdx, 0, AreasCount * sizeof(INT32));
	INT32 DupIdxCount = 0;

	BS->AddDWORD(AreasCount); // Ulozim pocet oblasti

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim data oblasti
		INT32 Idx = 0;
		for (INT32 j = 1; j <= 65535; j++) {
			INT32 I = i - j;
			if (I < 0) break;
			if (CompareAREA(AList1[i], AList1[I]) == 0) {
				Idx = j;
				break;
			}
		}
		DupIdx[i] = Idx;
	}

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim indexy
		BS->AddBIT(DupIdx[i] != 0);
	}
	BS->AlignWriteToBYTE(false);

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim indexy
		if (DupIdx[i] != 0) BS->AddWORD(DupIdx[i]);
	}

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim sirky
		if (DupIdx[i] == 0) BS->AddBYTE(AList1[i]->W - 1);
	}
	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim hlbky
		if (DupIdx[i] == 0) BS->AddBYTE(AList1[i]->H - 1);
	}

	UINT32 Old2 = 0;
	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim pozicie
		BS->AddDWORD(AList1[i]->AbsPos - Old2);
		Old2 = AList1[i]->AbsPos;
	}

	for (int x = 0; x < MAX_AREA_WIDTH_B; x++) {
		for (INT32 i = 0; i < AreasCount; i++) { // Ulozim hlbky
			if (DupIdx[i] == 0) SaveAREAData(BS, AList1[i], x);
		}
	}

	// Uvolnim pamat
	for (INT32 i = 0; i <= ADataCounter; i++) {
		if (AreasDataM[i] == NULL) break;
		delete AreasDataM[i];
	}
	delete AList1;
	delete DupIdx;

	UINT32 U32 = BS->GetCurrentLength();
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem nekomprimovanu dlzku
		delete BS;
		return CBI_ERROR_WRITE_FILE;
	}

	IBitStream *BComp = new IBitStream();
	U32 = CompressBitStream(BS, BComp); // Komprimujem
	if (fwrite(&U32, sizeof(UINT32), 1, (FILE*)FFile) != 1) { // Zapisem komprimovanu dlzku
		delete BS;
		delete BComp;
		return CBI_ERROR_WRITE_FILE;
	}

	bool B = fwrite(BComp->Data, 1, U32, (FILE*)FFile) == U32; // Zapisem komprimovane data
	pPageInfo->DATA_SIZE = BComp->GetCurrentLength() + 2 * 4; // Ulozim raw dlzku dat

	delete BS;
	delete BComp;

	if (B) Pages.Add(pPageInfo); else delete pPageInfo;
	return B ? 0 : CBI_ERROR_WRITE_FILE;
}
// .....................................................................................................................
// .....................................................................................................................
// Otvorenie streamu
// .....................................................................................................................
INT32 __fastcall ICBIStream1::Open(CONST_PCHAR pFileName) {

	INT32 R = ICBIStream::Open(pFileName); // Volam predchodcu
	if (R != 0) return R;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie stranky ako obrazku
// .....................................................................................................................
INT32 __fastcall ICBIStream1::GetPageImage(INT32 pIndex, IBitmap2 *Result) {
	if ((pIndex < 0) || (pIndex >= Pages.Count)) return CBI_ERROR_PAGE_OUT_OF_RANGE;
	PCBI_PAGE_HEADER PageInfo = (PCBI_PAGE_HEADER)Pages.Items[pIndex];

	if (_fseeki64((FILE*)FFile, PageInfo->DATA_OFFSET, SEEK_SET) != 0) return CBI_ERROR_READ_FILE; // Nastavim sa na zaciatok stranky

	Result->Create(PageInfo->WIDTH, PageInfo->HEIGHT); // Vytvorim stranku

	UINT32 U1, U2;
	if (fread(&U1, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam nekomprimovanu dlzku
	if (fread(&U2, sizeof(UINT32), 1, (FILE*)FFile) != 1) return CBI_ERROR_READ_FILE; // Nacitam komprimovanu dlzku

	IBitStream *BComp = new IBitStream();
	BComp->Allocate(U2);
	if (fread(BComp->Data, 1, U2, (FILE*)FFile) != U2) { // Nacitam komprimovane data do streamu
		delete BComp;
		return CBI_ERROR_READ_FILE;
	}

	IBitStream *BS = new IBitStream(); // Nacitam bitstream
	BS->Allocate(U1);
	if (DecompressBitStream(BComp, U1, BS) != U1) {
		delete BComp;
		delete BS;
		return CBI_ERROR_INVALID_STRUCTURE;
	}

	delete BComp; // Zrusim nepotrebny stream
	BS->Rewind(); // Nastavim sa na zaciatok

	INT32 AreasCount = BS->GetDWORD();
	PAREA AreasData = new AREA[AreasCount];
	INT32 *DupIdx = new INT32[AreasCount];

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim indexy
		DupIdx[i] = BS->GetBIT() ? 1 : 0;
	}
	BS->AlignReadToBYTE();

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim indexy
		if (DupIdx[i] != 0) DupIdx[i] = BS->GetWORD();
	}

	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim sirky
		if (DupIdx[i] == 0) AreasData[i].W = BS->GetBYTE() + 1;
		else AreasData[i].W = AreasData[i - DupIdx[i]].W;
	}
	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim hlbky
		if (DupIdx[i] == 0) AreasData[i].H = BS->GetBYTE() + 1;
		else AreasData[i].H = AreasData[i - DupIdx[i]].H;
	}

	UINT32 Old2 = 0;
	for (INT32 i = 0; i < AreasCount; i++) { // Ulozim pozicie
		AreasData[i].AbsPos = Old2 + BS->GetDWORD();
		AreasData[i].Y = AreasData[i].AbsPos / Result->Width;
		AreasData[i].X = AreasData[i].AbsPos - (AreasData[i].Y * Result->Width);
		Old2 = AreasData[i].AbsPos;
	}

	for (INT32 i = 0; i < AreasCount; i++) { // Vynulujem data ulozenych oblastni
		if (DupIdx[i] == 0) memset(AreasData[i].RawData, 0, DATA_AREA_LENGTH);
	}

	for (INT32 x = 0; x < MAX_AREA_WIDTH_B; x++) {
		for (INT32 i = 0; i < AreasCount; i++) { // Nacitam ulozene oblasti
			if (DupIdx[i] == 0) ReadAREAData(BS, AreasData + i, x);
		}
	}
	for (INT32 i = 0; i < AreasCount; i++) { // Skopirujem data rovnakych oblasti
		if (DupIdx[i] != 0) memcpy(AreasData[i].RawData, AreasData[i - DupIdx[i]].RawData, DATA_AREA_LENGTH);
	}

	for (INT32 i = 0; i < AreasCount; i++) {
		DrawArea(Result, AreasData + i, AreasData[i].X, AreasData[i].Y);
	}

	delete BS; // Uvolnim pamat
	delete AreasData;
	delete DupIdx;

	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Nastavenie pixela
// .....................................................................................................................
void __fastcall ICBIStream1::SetPixel(PAREA pArea, INT32 pX, INT32 pY) {
	BYTE M = 0x80; M >>= pX & 0x7;
	pArea->RawData[pY * MAX_AREA_WIDTH_B + (pX >> 3)] |= M;
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie hodnoty pixela
// .....................................................................................................................
bool __fastcall ICBIStream1::GetPixel(PAREA pArea, INT32 pX, INT32 pY) {
	BYTE M = 0x80; M >>= pX & 0x7;
	return (pArea->RawData[pY * MAX_AREA_WIDTH_B + (pX >> 3)] & M) != 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Porovnanie AREA
// .....................................................................................................................
INT32 __fastcall ICBIStream1::CompareAREA(PAREA P1, PAREA P2) {
	return memcmp(P1->RawData, P2->RawData, DATA_AREA_LENGTH);
}
// .....................................................................................................................
// .....................................................................................................................
// Vykreslenie oblasti
// .....................................................................................................................
void __fastcall ICBIStream1::DrawArea(IBitmap2 *pImage, PAREA pArea, INT32 pX, INT32 pY) {
	for (INT32 y = 0; y < pArea->H; y++) {
		for (INT32 x = 0; x < pArea->W; x++) {
			bool B = GetPixel(pArea, x, y);
			if (B) pImage->SetPixel(pX + x, pY + y, true);
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Inicializacia struktury SEARCH_AREA
// .....................................................................................................................
void __fastcall ICBIStream1::InitStructure(INT32 pX, INT32 pY, PSEARCH_AREA pValue) {
	pValue->Height = pValue->Width = 1;
	pValue->MinX = pValue->MaxX = pValue->X = pX;
	pValue->MinY = pValue->MaxY = pValue->Y = pY;
	pValue->MaskStartX = pX - MAX_AREA_WIDTH, pValue->MaskStartY = pY;
	memset(pValue->UsedMask, 0, 2 * MAX_AREA_WIDTH * MAX_AREA_HEIGHT);
}
// .....................................................................................................................
// .....................................................................................................................
// Presetrenie oblasti a vygenerovanie okrajov a vyplne
// .....................................................................................................................
void __fastcall ICBIStream1::InspectArea(PSEARCH_AREA pArea) {
	INT32 X = pArea->X; INT32 Y = pArea->Y;
	if (pArea->MinX > X) {
		if (pArea->Width == MAX_AREA_WIDTH) return;
		pArea->MinX = X; pArea->Width++;
	}
	if (pArea->MaxX < X) {
		if (pArea->Width == MAX_AREA_WIDTH) return;
		pArea->MaxX = X; pArea->Width++;
	}
	if (pArea->MinY > Y) {
		if (pArea->Height == MAX_AREA_HEIGHT) return;
		pArea->MinY = Y; pArea->Height++;
	}
	if (pArea->MaxY < Y) {
		if (pArea->Height == MAX_AREA_HEIGHT) return;
		pArea->MaxY = Y; pArea->Height++;
	}
	pArea->Image->SetPixel(X, Y, false); // Zrusim zaradeny pixel
	pArea->UsedMask[((Y - pArea->MaskStartY) * (2 * MAX_AREA_WIDTH)) + (X - pArea->MaskStartX)] = 1;

	if (pArea->Image->GetPixel(X, Y - 1)) {
		pArea->X = X; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X - 1, Y)) {
		pArea->X = X - 1; pArea->Y = Y;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y)) {
		pArea->X = X + 1; pArea->Y = Y;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X, Y + 1)) {
		pArea->X = X; pArea->Y = Y + 1;
		InspectArea(pArea);
	}

	if (pArea->Image->GetPixel(X - 1, Y - 1)) {
		pArea->X = X - 1; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y - 1)) {
		pArea->X = X + 1; pArea->Y = Y - 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X - 1, Y + 1)) {
		pArea->X = X - 1; pArea->Y = Y + 1;
		InspectArea(pArea);
	}
	if (pArea->Image->GetPixel(X + 1, Y + 1)) {
		pArea->X = X + 1; pArea->Y = Y + 1;
		InspectArea(pArea);
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Vytvorenie AREA z originalneho obrazku a SEARCH_AREA
// .....................................................................................................................
void __fastcall ICBIStream1::CreateAreaFromSearchStruct(IBitmap2 *pImage, PSEARCH_AREA pSearchArea, PAREA Result) {
	Result->X = pSearchArea->MinX; Result->Y = pSearchArea->MinY;
	Result->W = pSearchArea->Width; Result->H = pSearchArea->Height;
	Result->AbsPos = Result->Y * pImage->Width + Result->X;
	memset(Result->RawData, 0, DATA_AREA_LENGTH);

	for (INT32 y = 0; y < pSearchArea->Height; y++) {
		for (INT32 x = 0; x < pSearchArea->Width; x++) {
			INT32 X = pSearchArea->MinX + x;
			INT32 Y = pSearchArea->MinY + y;
			if (pSearchArea->UsedMask[(Y - pSearchArea->MaskStartY) * 2 * MAX_AREA_WIDTH + (X - pSearchArea->MaskStartX)] == 1) SetPixel(Result, x, y);
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Ulozenie area
// .....................................................................................................................
void __fastcall ICBIStream1::SaveAREAData(IBitStream *pStream, PAREA pArea, INT32 pCol) {
	if ((pArea->H > 1) && (pArea->W > 1)) {
		for (INT32 y = 0; y < pArea->H; y++) {
			for (INT32 x = pCol * 8; x < (pCol + 1) * 8; x++) {
				if (x >= pArea->W) continue;
				pStream->AddBIT(GetPixel(pArea, x, y));
			}
			pStream->AlignWriteToBYTE(false);
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie area
// .....................................................................................................................
void __fastcall ICBIStream1::ReadAREAData(IBitStream *pStream, PAREA pArea, INT32 pCol) {
	bool AllBlack = !((pArea->H > 1) && (pArea->W > 1));
	for (INT32 y = 0; y < pArea->H; y++) {
		for (INT32 x = pCol * 8; x < (pCol + 1) * 8; x++) {
			if (x >= pArea->W) continue;
			if (AllBlack) SetPixel(pArea, x, y);
			else {
				if (pStream->GetBIT()) SetPixel(pArea, x, y);
			}
		}
		if (!AllBlack) pStream->AlignReadToBYTE();
	}
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// IBitStream
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
IBitStream::IBitStream(void) {
	Data = NULL; // Inicializacia
	Capacity = 0;
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
IBitStream::~IBitStream(void) {
	Clear(); // Uvolnim pamat
}
// .....................................................................................................................
// .....................................................................................................................
// Uvolnenie objektu z pamate
// .....................................................................................................................
void __fastcall IBitStream::Clear(void) {
	if (Data != NULL) delete Data; // Zrusim data
	Data = NULL; // Inicializacia
	Capacity = 0;
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Realokacia pamate
// .....................................................................................................................
void __fastcall IBitStream::Reallocate(void) {
	Capacity += ALLOCATE_BUFFER_SIZE; // Urcim novu kapacitu
	PBYTE NW = new BYTE[Capacity]; // Alokujem novu pamat
	if (Data != NULL) { // Uz je alokovana pamat?
		memcpy(NW, Data, Capacity - ALLOCATE_BUFFER_SIZE); // Skopirujem pamat
		delete Data; // Zrusim staru pamat
	}
	Data = NW; // Ulozim ako novu pamat
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie bitu
// .....................................................................................................................
void __fastcall IBitStream::AddBIT(bool pValue) {
	_AddBIT(pValue); // Volam protected metodu
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie bitu
// .....................................................................................................................
void __fastcall IBitStream::_AddBIT(bool pValue) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) Reallocate(); // Treba alokovat?
		Data[FCurrentIndex] = 0; // Inicializujem
	}
	if (pValue) Data[FCurrentIndex] |= FCurrentMask; // Nastavim bit ak je vstup 1

	if (FCurrentMask == 0x1) { // Posledny bit v bajte?
		FCurrentMask = 0x80; // Zaciatok
		FCurrentIndex++; 
	}
	else FCurrentMask >>= 1; // Zrolujem masku
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT8
// .....................................................................................................................
void __fastcall IBitStream::AddBYTE(BYTE pValue) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) Reallocate(); // Treba alokovat?
		Data[FCurrentIndex++] = pValue; // Inicializujem
	}
	else {
		BYTE M = 0x80;
		for (INT32 i = 0; i < 8; i++) { // Zaradim jednotlive bity zadanej hodnoty
			_AddBIT((M & pValue) != 0);
			M >>= 1;
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Pretocenie streamu na zaciatok
// .....................................................................................................................
void __fastcall IBitStream::Rewind(void) {
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT16
// .....................................................................................................................
void __fastcall IBitStream::AddWORD(UINT16 pValue) {
	AddBYTE((pValue >> 8) & 0xff);
	AddBYTE(pValue & 0xff);
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT32
// .....................................................................................................................
void __fastcall IBitStream::AddDWORD(UINT32 pValue) {
	AddWORD((pValue >> 16) & 0xffff);
	AddWORD(pValue & 0xffff);
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT64
// .....................................................................................................................
void __fastcall IBitStream::AddQWORD(UINT64 pValue) {
	AddDWORD((pValue >> 32) & 0xffffffff);
	AddDWORD(pValue & 0xffffffff);
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie bitu
// .....................................................................................................................
bool __fastcall IBitStream::_GetBIT(void) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) return false; // Som mimo?
	}
	bool B = (Data[FCurrentIndex] & FCurrentMask) != 0;
	if (FCurrentMask == 0x1) { // Posledny bit v bajte?
		FCurrentMask = 0x80; // Zaciatok
		FCurrentIndex++; 
	}
	else FCurrentMask >>= 1; // Zrolujem masku
	return B;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie bitu
// .....................................................................................................................
bool __fastcall IBitStream::GetBIT(void) {
	return _GetBIT(); // Volam protected metodu
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT8
// .....................................................................................................................
BYTE __fastcall IBitStream::GetBYTE(void) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) return 0; // Som uz mimo?
		return Data[FCurrentIndex++]; // Vratim naraz 8 bitov
	}
	else {
		BYTE M = 0x80; BYTE Res = 0; 
		for (INT32 i = 0; i < 8; i++) { // Nacitam 8 bitov
			if (_GetBIT()) Res |= M;
			M >>= 1;
		}
		return Res; // Vratim poskladany vysledok
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT16
// .....................................................................................................................
UINT16 __fastcall IBitStream::GetWORD(void) {
	UINT16 Res = ((UINT16)GetBYTE()) << 8;
	Res |= (UINT16)GetBYTE();
	return Res;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT32
// .....................................................................................................................
UINT32 __fastcall IBitStream::GetDWORD(void) {
	UINT32 Res = ((UINT32)GetWORD()) << 16;
	Res |= (UINT32)GetWORD();
	return Res;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT64
// .....................................................................................................................
UINT64 __fastcall IBitStream::GetQWORD(void) {
	UINT64 Res = ((UINT64)GetDWORD()) << 32;
	Res |= (UINT64)GetDWORD();
	return Res;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie bufra
// .....................................................................................................................
void __fastcall IBitStream::AddBuffer(INT32 pLength, PBYTE pData) {
	for (INT32 i = 0; i < pLength; i++) AddBYTE(pData[i]);
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie X-bitoveho slova
// .....................................................................................................................
void __fastcall IBitStream::AddXBIT(INT32 pBitsCount, UINT64 pData) {
	UINT64 M = 0x1; M <<= pBitsCount - 1;
	for (INT32 i = 0; i < pBitsCount; i++) {
		_AddBIT((pData & M) != 0);
		M >>= 1;
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Ulozenie streamu do suboru
// .....................................................................................................................
INT32 __fastcall IBitStream::SaveToFile(CONST_PCHAR pFileName) {
	FILE *F = fopen(pFileName, "wb+");
	if (F == NULL) return -1;

	INT32 L = FCurrentMask == 0x80 ? FCurrentIndex : FCurrentIndex + 1;
	if (L > 0) fwrite(Data, 1, L, F);

	fclose(F);
	return 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Zarovnanie zapisu na byte
// .....................................................................................................................
void __fastcall IBitStream::AlignWriteToBYTE(bool pBit) {
	while(FCurrentMask != 0x80) _AddBIT(pBit);
}
// .....................................................................................................................
// .....................................................................................................................
// Ziskanie aktualnej dlzky
// .....................................................................................................................
INT32 __fastcall IBitStream::GetCurrentLength(void) {
	return FCurrentMask == 0x80 ? FCurrentIndex : FCurrentIndex + 1;
}
// .....................................................................................................................
// .....................................................................................................................
// Alokacia internej pamate
// .....................................................................................................................
void __fastcall IBitStream::Allocate(INT32 pCapacity) {
	if (Capacity >= pCapacity) return;
	PBYTE NW = new BYTE[pCapacity]; // Alokujem novu pamat
	if (Data != NULL) { // Uz je alokovana pamat?
		memcpy(NW, Data, Capacity); // Skopirujem pamat
		delete Data; // Zrusim staru pamat
	}
	Data = NW; // Ulozim ako novu pamat
	Capacity = pCapacity; // Urcim novu kapacitu
}
// .....................................................................................................................
// .....................................................................................................................
// Zarovnanie citania na byte
// .....................................................................................................................
void __fastcall IBitStream::AlignReadToBYTE(void) {
	while(FCurrentMask != 0x80) _GetBIT();
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// IBitmap2
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// ................................................................................................
// Konstruktor
// ................................................................................................
IBitmap2::IBitmap2(void) {
	RawData = NULL; // Inicializacia bufrov
	Rows = NULL;
	Clear(); // Inicializujem premenne
}
// ................................................................................................
// ................................................................................................
// Destruktor
// ................................................................................................
IBitmap2::~IBitmap2(void) {
	Clear();
}
// ................................................................................................
// ................................................................................................
// Uvolnenie objektu z pamate
// ................................................................................................
void __fastcall IBitmap2::Clear(void) {

	if (RawData != NULL) delete RawData; // Zrusim bufre
	if (Rows != NULL) delete Rows;

	RawData = NULL; // Inicializacia bufrov
	Rows = NULL;

	Width = Height = 0; // Inicializacia premennych
	RowSize = 0;
	Pallete[0] = 0xffffff; Pallete[1] = 0;
	RawDataSize = 0;
	Count0 = Count1 = 0;
	ValidRowSize = 0;
	LastRowByteMask = 0;
}
// ................................................................................................
// ................................................................................................
// Nacitanie obsahu z BMP suboru
// ................................................................................................
INT32 __fastcall IBitmap2::LoadFromBMP(CONST_PCHAR pFileName) {
	Clear(); // Uvolnim aktualny obsah

	FILE *F = fopen(pFileName, "rb"); // Otvorim subor
	if (F == NULL) return BITMAP2_IMAGE_ERROR_OPEN_FILE;

	BMP_FILE_HEADER Header;
	if (fread(&Header, sizeof(BMP_FILE_HEADER), 1, F) != 1) { // Nacitam hlavicku
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_READ_FILE;
	}

	if ((Header.Magic1 != 'B') && (Header.Magic2 != 'M')) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_FILE_HEADER_CORRUPTED;
	}
	if ((Header.NPlanes != 1) || (Header.HeaderSize != 40)) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_FILE_HEADER_CORRUPTED;
	}
	if ((Header.Width < 1) || (Header.Height < 1)) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_FILE_HEADER_CORRUPTED;
	}	
	if (Header.BitsPP != 1) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_UNSUPORTED_BITS_PER_PIXEL;
	}
	if (Header.CompressType != 0) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_UNSUPORTED_COMPRESSION;
	}

	fseek(F, 14 + Header.HeaderSize, 0L); // Nastavim sa na paletu
	if (fread(Pallete, sizeof(COLORREF), 2, F) != 2) { // Nacitam paletu
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_READ_FILE;
	}

	fseek(F, Header.BmpOffset, 0L); // Nastavim sa na pixely

	Width = Header.Width; Height = Header.Height; // Ulozim rozmery
	RowSize = ALIGN_32(Width); RowSize >>= 3;
	RawDataSize = Height * RowSize; // Urcim velkost dat

	ValidRowSize = Width >> 3; // Urcim pocet platnych bajtov v riadku a bitovu masku posledneho
	if (ValidRowSize << 3 != Width) {
		LastRowByteMask = MASK_TABLE_1[Width - (ValidRowSize << 3)];
		ValidRowSize++;
	}
	else LastRowByteMask = 0xff;

	if (RawDataSize != Header.BmpByteSize) {
		fclose(F); // Zatvorim subor
		return BITMAP2_IMAGE_ERROR_FILE_HEADER_CORRUPTED;
	}

	RawData = new BYTE[RawDataSize]; // Alokujem pamat
	Rows = new PBYTE[Height]; // Alokujem pole pre zaciatky riadkov
	Count0 = Count1 = 0; // Inicializujem pocitadla

	PBYTE CurrentRow = RawData + (Height - 1) * RowSize; // Inicializujem smernik na posledny riadok
	INT32 C = ValidRowSize - 1; // Pomocne pocitadlo
	INT32 C1;

	for (INT32 i = Height - 1; i >= 0; i--) {
		if (fread(CurrentRow, 1, RowSize, F) != RowSize) {
			fclose(F); // Zatvorim subor
			return BITMAP2_IMAGE_ERROR_READ_FILE;
		}
		Rows[i] = CurrentRow; // Ulozim zaciatok riadku
		
		CurrentRow[ValidRowSize - 1] &= LastRowByteMask; // Nulujem nadbytocne bity
		for (INT32 j = ValidRowSize; j < RowSize; j++) CurrentRow[j] = 0;

		for (INT32 j = 0; j < C; j++) { // Pocitam pocty jednotiek a nul
			C1 = COUNT_TABLE_1[CurrentRow[j]];
			Count1 += C1; Count0 += 8 - C1;
		}
		
		C1 = COUNT_TABLE_1[CurrentRow[C] & LastRowByteMask]; // Posledny platny byte
		Count1 += C1; Count0 += 8 - C1;

		CurrentRow -= RowSize; // Predchadzajuci riadok
	}

	if (Count1 > Count0) { // Treba vymenit bity? 0 - pozadie, 1 - popredie
		COLORREF CC = Pallete[0]; Pallete[0] = Pallete[1]; Pallete[1] = CC; // Vymenim farby
		
		CurrentRow = RawData;
		for (INT32 i = 0; i < Height; i++) {
			for (INT32 j = 0; j < C; j++) CurrentRow[j] ^= 0xff; // Preklopim bity
			CurrentRow[C] ^= LastRowByteMask & 0xff; // Posledny platny byte
			CurrentRow += RowSize; // Nasledujuci riadok
		}
		CC = Count1; Count1 = Count0; Count0 = CC;
	}

	fclose(F); // Zatvorim subor
	return 0; // Vsetko OK
}
// ................................................................................................
// ................................................................................................
// Ulozenie obsahu do BMP suboru
// ................................................................................................
INT32 __fastcall IBitmap2::SaveToBMP(CONST_PCHAR pFileName) {
	BMP_FILE_HEADER Header;
	
	Header.Magic1 = 'B'; Header.Magic2 = 'M'; // Vyplnim strukturu
	Header.Reserver1 = Header.Reserver2 = 0;
	Header.BitsPP = 1; Header.BmpByteSize = RawDataSize; Header.BmpOffset = sizeof(BMP_FILE_HEADER) + 2*sizeof(COLORREF);
	Header.CompressType = 0; Header.HeaderSize = 40; 
	Header.Height = Height; Header.Width = Width; 
	Header.HRes = Header.VRes = 0; Header.NPlanes = 1; Header.NumOfColors = 2; Header.NumOfImpColors = 0;
	Header.FileSize = Header.BmpOffset + RawDataSize;

	FILE *F = fopen(pFileName, "wb+"); // Vytvorim subor
	if (F == NULL) return BITMAP2_IMAGE_ERROR_CREATE_FILE;

	if (fwrite(&Header, sizeof(BMP_FILE_HEADER), 1, F) != 1) {
		fclose(F); // Uzatvorim subor
		return BITMAP2_IMAGE_ERROR_WRITE_FILE;
	}
	if (fwrite(Pallete, sizeof(COLORREF), 2, F) != 2) {
		fclose(F); // Uzatvorim subor
		return BITMAP2_IMAGE_ERROR_WRITE_FILE;
	}

	PBYTE CurrentRow = RawData + (Height - 1) * RowSize; // Inicializujem smernik na posledny riadok
	for (INT32 i = 0; i < Height; i++) {
		if (fwrite(CurrentRow, 1, RowSize, F) != RowSize) {
			fclose(F); // Zatvorim subor
			return BITMAP2_IMAGE_ERROR_WRITE_FILE;
		}
		CurrentRow -= RowSize; // Predchadzajuci riadok
	}

	fclose(F); // Uzatvorim subor
	return 0; // Vsetko OK
}
// ................................................................................................
// ................................................................................................
// Ziskanie hodnoty pixela na zadanej pozicii
// ................................................................................................
bool __fastcall IBitmap2::GetPixel(INT32 pX, INT32 pY) {
	if ((pX < 0) || (pX >= Width)) return false;
	if ((pY < 0) || (pY >= Height)) return false;
	BYTE M = 0x80; M >>= pX & 0x7;
	return (*(Rows[pY] + (pX >> 3)) & M) != 0;
}
// ................................................................................................
// ................................................................................................
// Nastavenie hodnoty pixela na zadanej pozicii
// ................................................................................................
void __fastcall IBitmap2::SetPixel(INT32 pX, INT32 pY, bool pValue) {
	if ((pX < 0) || (pX >= Width)) return;
	if ((pY < 0) || (pY >= Height)) return;
	BYTE M = 0x80; M >>= pX & 0x7;
	if (pValue) *(Rows[pY] + (pX >> 3)) |= M;
	else *(Rows[pY] + (pX >> 3)) &= (~M);
}
// ................................................................................................
// ................................................................................................
// Vytvorenie kopie aktualneho obrazku
// ................................................................................................
IBitmap2* __fastcall IBitmap2::Clone(void) {
	IBitmap2 *Result = new IBitmap2();
	if (RawData == NULL) return Result; // Je obsah prazdny?

	Result->Width = Width; // Skopirujem premenne
	Result->Height = Height; 
	Result->RowSize = RowSize;
	Result->Pallete[0] = Pallete[0];
	Result->Pallete[1] = Pallete[1];
	Result->RawDataSize = RawDataSize;
	Result->Count0 = Count0; 
	Result->Count1 = Count1;
	Result->ValidRowSize = ValidRowSize;
	Result->LastRowByteMask = LastRowByteMask;

	Result->RawData = new BYTE[RawDataSize]; memcpy(Result->RawData, RawData, RawDataSize); // Skopirujem raw data
	Result->Rows = new PBYTE[Height];
	PBYTE P = Result->RawData;
	for (INT32 i = 0; i < Height; i++) { // Vytvorim mapu riadkov
		Result->Rows[i] = P; P += RowSize;
	}

	return Result;
}
// ................................................................................................
// ................................................................................................
// Vytvorenie prazdneho obrazku
// ................................................................................................
void __fastcall IBitmap2::Create(INT32 pWidth, INT32 pHeight) {
	Clear(); // Uvolnim aktualny obsah

	Width = pWidth; // Ulozim rozmery
	Height = pHeight; 

	RowSize = ALIGN_32(Width); RowSize >>= 3;
	RawDataSize = Height * RowSize; // Urcim velkost dat

	ValidRowSize = Width >> 3; // Urcim pocet platnych bajtov v riadku a bitovu masku posledneho
	if (ValidRowSize << 3 != Width) {
		LastRowByteMask = MASK_TABLE_1[Width - (ValidRowSize << 3)];
		ValidRowSize++;
	}
	else LastRowByteMask = 0xff;
	Count0 = Height * Width; Count1 = 0;

	RawData = new BYTE[RawDataSize]; memset(RawData, 0, RawDataSize); // Vytvorim a inicializujem raw data
	Rows = new PBYTE[Height];
	PBYTE P = RawData;
	for (INT32 i = 0; i < Height; i++) { // Vytvorim mapu riadkov
		Rows[i] = P; P += RowSize;
	}
}
// ................................................................................................
// ................................................................................................
// Najdenie dalsieho vyskytu bodu popredia
// ................................................................................................
bool __fastcall IBitmap2::FindNextForegroundPixel(INT32 &X, INT32 &Y) {
	if ((X == -1) || (Y == -1)) { // Zaciatok?
		X = -1; Y = 0;
	}
	while(IncreaseXPosition(X, Y)) { // Pokial sa da testovat
		if (GetPixel(X, Y)) return true;
	}
	return false; // Uz nie je bod
}
// ................................................................................................
// ................................................................................................
// Zvysenie pozicie X
// ................................................................................................
bool __fastcall IBitmap2::IncreaseXPosition(INT32 &X, INT32 &Y) {
	X++; // Zvysim X
	if (X != Width) return true; // Nepretieklo?
	X = 0; Y++; // Zvysim Y
	return Y != Height; // Vratim priznak pretecenia Y
}
// ................................................................................................
// ................................................................................................
// Ulozenie obsahu do bufra vo formate BMP suboru
// ................................................................................................
PBYTE __fastcall IBitmap2::SaveToBMPBuffer(UINT32 &ResultLength) {
	BMP_FILE_HEADER Header;
	
	Header.Magic1 = 'B'; Header.Magic2 = 'M'; // Vyplnim strukturu
	Header.Reserver1 = Header.Reserver2 = 0;
	Header.BitsPP = 1; Header.BmpByteSize = RawDataSize; Header.BmpOffset = sizeof(BMP_FILE_HEADER) + 2*sizeof(COLORREF);
	Header.CompressType = 0; Header.HeaderSize = 40; 
	Header.Height = Height; Header.Width = Width; 
	Header.HRes = Header.VRes = 0; Header.NPlanes = 1; Header.NumOfColors = 2; Header.NumOfImpColors = 0;
	Header.FileSize = Header.BmpOffset + RawDataSize;

	PBYTE ResultData = new BYTE[Header.FileSize];
	INT32 Offset = 0;
	memcpy(ResultData + Offset, &Header, sizeof(BMP_FILE_HEADER)); Offset += sizeof(BMP_FILE_HEADER);
	memcpy(ResultData + Offset, Pallete, 2*sizeof(COLORREF)); Offset += 2*sizeof(COLORREF);

	PBYTE CurrentRow = RawData + (Height - 1) * RowSize; // Inicializujem smernik na posledny riadok
	for (INT32 i = 0; i < Height; i++) {
		memcpy(ResultData + Offset, CurrentRow, RowSize); Offset += RowSize;
		CurrentRow -= RowSize; // Predchadzajuci riadok
	}

	ResultLength = Offset;
	return ResultData; // Vsetko OK
}
// ................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// IStopWatch
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
IStopWatch::IStopWatch(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
IStopWatch::~IStopWatch(void) {
}
// .....................................................................................................................
// .....................................................................................................................
// Spustenie merania
// .....................................................................................................................
void __fastcall IStopWatch::StartMeassure(void) {
	FStart = clock(); // Ulozim zaciatok merania
}
// .....................................................................................................................
// .....................................................................................................................
// Ukoncenie merania
// .....................................................................................................................
void __fastcall IStopWatch::EndMeassure(INT32 pRunCount) {
	FEnd = clock(); // Ulozim koniec merania
	FRunCount = pRunCount; // Ulozim pocet iteracii
}
// .....................................................................................................................
// .....................................................................................................................
// Pocet iteracii v merani
// .....................................................................................................................
INT32 __fastcall IStopWatch::RunCount(void) {
	return FRunCount; // Vratim pocet iteracii
}
// .....................................................................................................................
// .....................................................................................................................
// Pocet uplynutych sekund v merani
// .....................................................................................................................
DOUBLE __fastcall IStopWatch::ElapsedTime(void) {
	return (DOUBLE)((FEnd - FStart) / (DOUBLE)CLOCKS_PER_SEC);
}
// .....................................................................................................................
// .....................................................................................................................
// Vykonnost posledneho merania
// .....................................................................................................................
DOUBLE __fastcall IStopWatch::Performance(void) {
	return (DOUBLE)RunCount() / ElapsedTime();
}
// .....................................................................................................................
// .....................................................................................................................
// Vypisanie vysledkov merania
// .....................................................................................................................
void __fastcall IStopWatch::PrintResultMessage(void) {
	printf("\nResult: elapsed time: %.4lf sec, iterations: %d, performance: %.0lf iterations per sec. Execution time of one iteration: %.4lf msec.\n", (DOUBLE)ElapsedTime(), RunCount(), (DOUBLE)Performance(), (DOUBLE)((1.0 / Performance()) * 1000.0));
}
// .....................................................................................................................

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// paq9a
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

static const BYTE State_table[256][2]={
{  1,  2},{  3,  5},{  4,  6},{  7, 10},{  8, 12},{  9, 13},{ 11, 14}, // 0
{ 15, 19},{ 16, 23},{ 17, 24},{ 18, 25},{ 20, 27},{ 21, 28},{ 22, 29}, // 7
{ 26, 30},{ 31, 33},{ 32, 35},{ 32, 35},{ 32, 35},{ 32, 35},{ 34, 37}, // 14
{ 34, 37},{ 34, 37},{ 34, 37},{ 34, 37},{ 34, 37},{ 36, 39},{ 36, 39}, // 21
{ 36, 39},{ 36, 39},{ 38, 40},{ 41, 43},{ 42, 45},{ 42, 45},{ 44, 47}, // 28
{ 44, 47},{ 46, 49},{ 46, 49},{ 48, 51},{ 48, 51},{ 50, 52},{ 53, 43}, // 35
{ 54, 57},{ 54, 57},{ 56, 59},{ 56, 59},{ 58, 61},{ 58, 61},{ 60, 63}, // 42
{ 60, 63},{ 62, 65},{ 62, 65},{ 50, 66},{ 67, 55},{ 68, 57},{ 68, 57}, // 49
{ 70, 73},{ 70, 73},{ 72, 75},{ 72, 75},{ 74, 77},{ 74, 77},{ 76, 79}, // 56
{ 76, 79},{ 62, 81},{ 62, 81},{ 64, 82},{ 83, 69},{ 84, 71},{ 84, 71}, // 63
{ 86, 73},{ 86, 73},{ 44, 59},{ 44, 59},{ 58, 61},{ 58, 61},{ 60, 49}, // 70
{ 60, 49},{ 76, 89},{ 76, 89},{ 78, 91},{ 78, 91},{ 80, 92},{ 93, 69}, // 77
{ 94, 87},{ 94, 87},{ 96, 45},{ 96, 45},{ 48, 99},{ 48, 99},{ 88,101}, // 84
{ 88,101},{ 80,102},{103, 69},{104, 87},{104, 87},{106, 57},{106, 57}, // 91
{ 62,109},{ 62,109},{ 88,111},{ 88,111},{ 80,112},{113, 85},{114, 87}, // 98
{114, 87},{116, 57},{116, 57},{ 62,119},{ 62,119},{ 88,121},{ 88,121}, // 105
{ 90,122},{123, 85},{124, 97},{124, 97},{126, 57},{126, 57},{ 62,129}, // 112
{ 62,129},{ 98,131},{ 98,131},{ 90,132},{133, 85},{134, 97},{134, 97}, // 119
{136, 57},{136, 57},{ 62,139},{ 62,139},{ 98,141},{ 98,141},{ 90,142}, // 126
{143, 95},{144, 97},{144, 97},{ 68, 57},{ 68, 57},{ 62, 81},{ 62, 81}, // 133
{ 98,147},{ 98,147},{100,148},{149, 95},{150,107},{150,107},{108,151}, // 140
{108,151},{100,152},{153, 95},{154,107},{108,155},{100,156},{157, 95}, // 147
{158,107},{108,159},{100,160},{161,105},{162,107},{108,163},{110,164}, // 154
{165,105},{166,117},{118,167},{110,168},{169,105},{170,117},{118,171}, // 161
{110,172},{173,105},{174,117},{118,175},{110,176},{177,105},{178,117}, // 168
{118,179},{110,180},{181,115},{182,117},{118,183},{120,184},{185,115}, // 175
{186,127},{128,187},{120,188},{189,115},{190,127},{128,191},{120,192}, // 182
{193,115},{194,127},{128,195},{120,196},{197,115},{198,127},{128,199}, // 189
{120,200},{201,115},{202,127},{128,203},{120,204},{205,115},{206,127}, // 196
{128,207},{120,208},{209,125},{210,127},{128,211},{130,212},{213,125}, // 203
{214,137},{138,215},{130,216},{217,125},{218,137},{138,219},{130,220}, // 210
{221,125},{222,137},{138,223},{130,224},{225,125},{226,137},{138,227}, // 217
{130,228},{229,125},{230,137},{138,231},{130,232},{233,125},{234,137}, // 224
{138,235},{130,236},{237,125},{238,137},{138,239},{130,240},{241,125}, // 231
{242,137},{138,243},{130,244},{245,135},{246,137},{138,247},{140,248}, // 238
{249,135},{250, 69},{ 80,251},{140,252},{249,135},{250, 69},{ 80,251}, // 245
{140,252},{  0,  0},{  0,  0},{  0,  0}};  // 252
#define nex(state,sel) State_table[state][sel]

Squash *squash;
Stretch *stretch;
Ilog *ilog;
UINT32 MEM=1<<23;  // Global memory limit, 1 << 22+(memory option)

int StateMap::dt[1024] = {0};

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Squash::Squash() {
	static const INT32 t[33]={ 1,2,3,6,10,16,27,45,73,120,194,310,488,747,1101, 1546,2047,2549,2994,3348,3607,3785,3901,3975,4022,4050,4068,4079,4085,4089,4092,4093,4094};
	for (INT32 i = -2048; i < 2048; ++i) {
		INT32 w = i & 127;
		INT32 d= (i>>7) + 16;
		tab[i + 2048] = (t[d] * (128 - w) + t[(d + 1)] *w + 64) >> 7;
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Operator ()
// .....................................................................................................................
int __fastcall Squash::operator()(INT32 d) {
	d += 2048;
	if (d < 0) return 0;
	else if (d > 4095) return 4095;
	else return tab[d];
}
// .....................................................................................................................
// .....................................................................................................................
// Operator ()
// .....................................................................................................................
INT32 __fastcall Stretch::operator()(INT32 p) const {
	return t[p];
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Stretch::Stretch(void) {
	INT32 pi=0;
	for (INT32 x = -2047; x <= 2047; ++x) {  // invert squash()
		INT32 i = (*squash)(x);
		for (INT32 j = pi; j <= i; ++j) t[j] = x;
		pi = i + 1;
	}
	t[4095] = 2047;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Ilog::Ilog() {
	t = new BYTE[65536];
	UINT32 x = 14155776;
	for (INT32 i = 2; i < 65536; ++i) {
		x += 774541002 / (i * 2 - 1);  // numerator is 2^29/ln 2
		t[i] = x>>24;
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda llog
// .....................................................................................................................
inline INT32 llog(UINT32 x) {
	if (x >= 0x1000000)
		return 256 + (*ilog)(x >> 16);
	else if (x >= 0x10000)
		return 128 + (*ilog)(x >> 8);
	else
    return (*ilog)(x);
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor 
// .....................................................................................................................
Ilog::~Ilog(void) {
	delete t;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
StateMap::StateMap(INT32 n): N(n), cxt(0) {
	t = new UINT32[N];
	for (INT32 i = 0; i < N; ++i) t[i] = 1<<31;
	if (dt[0] == 0)
		for (INT32 i = 0; i < 1024; ++i) dt[i] = 16384 / (i + i + 3);
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda Update()
// .....................................................................................................................
void __fastcall StateMap::update(INT32 y, INT32 limit) {
	INT32 n = t[cxt] & 1023, p = t[cxt] >> 10;  // count, prediction
	if (n < limit) ++t[cxt];
	else t[cxt] = t[cxt] & 0xfffffc00 | limit;
	t[cxt] += (((y << 22) - p) >> 3) * dt[n] & 0xfffffc00;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda p()
// .....................................................................................................................
int StateMap::p(INT32 cx) {
	return t[cxt = cx] >> 20;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
StateMap::~StateMap(void) {
	delete t;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda pp()
// .....................................................................................................................
INT32 __fastcall Mix::pp(INT32 p1, INT32 p2, INT32 cx) {
	cxt = cx*2;
	return pr = ((x1 = p1)*(wt[cxt] >> 16) + (x2 = p2)*(wt[cxt + 1] >> 16) + 128) >> 8;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda update()
// .....................................................................................................................
void __fastcall Mix::update(INT32 y) {
	INT32 err = ((y << 12) - (*squash)(pr));
	if ((wt[cxt] & 3) < 3)
		err *= 4 - (++wt[cxt] & 3);
	err = (err + 8) >> 4;
	wt[cxt] += x1 * err & -4;
	wt[cxt + 1] += x2 * err;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Mix::Mix(INT32 n): N(n), x1(0), x2(0), cxt(0), pr(0) {
	wt = new INT32[n*2];
	for (INT32 i = 0; i < N * 2; ++i)
    wt[i]= 1<< 23;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
Mix::~Mix(void) {
	delete wt;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
APM::APM(INT32 n): Mix(n) {
	for (INT32 i = 0; i < n; ++i) wt[2 * i] = 0;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
template <int B> HashTable<B>::HashTable(INT32 n): t(0), N(n) {
	t1 = new BYTE[N + B * 4 + 64];
	t = t1 + 64 - INT32(((INT32)t) & 63);  // align on cache line boundary
}
// .....................................................................................................................
// .....................................................................................................................
// Operator []
// .....................................................................................................................
template <int B> inline PBYTE HashTable<B>::operator[](UINT32 i) {
  i *= 123456791;
  i = i << 16 | i >> 16;
  i *= 234567891;
  int chk = i >> 24;
  i = i * B & N - B;
  if (t[i] == chk) return t + i;
  if (t[i ^ B] == chk) return t + (i ^ B);
  if (t[i ^ B * 2] == chk) return t + (i ^ B * 2);
  if (t[i + 1] > t[i + 1 ^ B] || t[i + 1]>t[i + 1 ^ B * 2]) i ^= B;
  if (t[i + 1]>t[i + 1 ^ B ^ B * 2]) i ^= B ^ B * 2;
  memset(t + i, 0, B);
  t[i] = chk;
  return t + i;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
template <int B> HashTable<B>::~HashTable() {
	delete t1;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
LZP::LZP(): N(MEM/8), H(MEM/32), match(-1), len(0), pos(0), h(0), h1(0), h2(0), sm1(0x200), a1(0x10000), a2(0x40000), a3(0x100000), literals(0), matches(0), word0(0), word1(0) {
	buf = new BYTE[N];
	t = new UINT32[H];
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
LZP::~LZP() {
	delete buf;
	delete t;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda c()
// .....................................................................................................................
inline int __fastcall LZP::c() {
  return len >= MINLEN ? buf[match & N - 1] : -1;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda c()
// .....................................................................................................................
inline int __fastcall LZP::c(INT32 i) {
  return buf[pos - i & N - 1];
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda p()
// .....................................................................................................................
INT32 __fastcall LZP::p() {
  if (len < MINLEN) return 0;
  int cxt = len;
  if (len > 28) cxt = 28 +(len >= 32) + (len >= 64) + (len >= 128);
  int pc = c();
  int pr = sm1.p(cxt);
  pr = (*stretch)(pr);
  pr = (a1.pp(2048, pr * 2, h2 * 256 + pc & 0xffff) * 3 + pr) >> 2;
  pr = (a2.pp(2048, pr * 2, h1 * (11 << 6) + pc & 0x3ffff) * 3 + pr) >> 2;
  pr = (a3.pp(2048, pr * 2, h1 * (7 << 4) + pc & 0xfffff) * 3 + pr) >> 2;
  pr = (*squash)(pr);
  return pr;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda update()
// .....................................................................................................................
void __fastcall LZP::update(INT32 ch) {
  int y = c() == ch;     // 1 if prediction of ch was right, else 0
  h1 = h1 * (3 << 4) + ch + 1; // update context hashes
  h2 = h2 << 8 | ch;
  h = h * (5 << 2) + ch + 1 & H - 1;
  if (len >= MINLEN) {
    sm1.update(y);
    a1.update(y);
    a2.update(y);
    a3.update(y);
  }
  if (isalpha(ch))
    word0 = word0 * (29 << 2) + tolower(ch);
  else if (word0)
    word1 = word0, word0 = 0;
  buf[pos & N - 1] = ch;   // update buf
  ++pos;
  if (y) {  // extend match
    ++len;
    ++match;
    ++matches;
  }
  else {  // find new match, try order 6 context first
    ++literals;
    y = 0;
    len = 1;
    match = t[h];
    if (!((match ^ pos) & N - 1)) --match;
    while (len <= 128 && buf[match - len & N - 1] == buf[pos - len & N - 1]) ++len;
    --len;
  }
  t[h] = pos;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Predictor::Predictor(LZP *pObj): c0(0), nibble(1), bcount(0), t(MEM/2), a1(0x10000), a2(0x10000), a3(0x10000), lzp(pObj) {
	t2 = new BYTE[0x40000];
	for (INT32 i = 0; i < N; ++i) sp[i] = cp[i] = t2;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
Predictor::~Predictor(void) {
	delete t2;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda update()
// .....................................................................................................................
void __fastcall Predictor::update(int y) {
  if (c0 == 0) c0 = 1 - y;
  else {
    *sp[0] = nex(*sp[0], y);
    sm[0].update(y);
    for (INT32 i = 1; i < N; ++i) {
      *sp[i] = nex(*sp[i], y);
      sm[i].update(y);
      m[i-1].update(y);
    }
    c0 += c0 + y;
    if (++bcount == 8) bcount = c0 = 0;
    if ((nibble += nibble + y) >= 16) nibble = 1;
    a1.update(y);
    a2.update(y);
    a3.update(y);
  }
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda p()
// .....................................................................................................................
INT32 __fastcall Predictor::p() {
  if (c0 == 0) return lzp->p();
  else {
    int pc = lzp->c();  // mispredicted byte
    int r = ((pc + 256) >> (8 - bcount)) == c0;  // c0 consistent with mispredicted byte?
    UINT32 c4 = lzp->c4();  // last 4 whole context bytes, shifted into LSB
    UINT32 c8 = (lzp->c8() << 4) - 1;  // hash of last 7 bytes with 4 trailing 1 bits
    if ((bcount & 3) == 0) {  // nibble boundary?  Update context pointers
      pc &= -r;
      UINT32 c4p = c4 << 8;
      if (bcount == 0) {  // byte boundary?  Update order-1 context pointers
        cp[0] = t2 + (c4 >> 16 & 0xff00);
        cp[1] = t2 + (c4 >>8 & 0xff00) + 0x10000;
        cp[2] = t2 + (c4 & 0xff00) + 0x20000;
        cp[3] = t2 + (c4 << 8 & 0xff00) + 0x30000;
      }
      cp[4] = t[(c4p & 0xffff00) - c0];
      cp[5] = t[(c4p & 0xffffff00) * 3 + c0];
      cp[6] = t[c4 * 7 + c0];
      cp[7] = t[(c8 * 5 & 0xfffffc) + c0];
      cp[8] = t[(c8 * 11 & 0xffffff0) + c0 + pc * 13];
      cp[9] = t[lzp->word0 * 5 + c0 + pc * 17];
      cp[10] = t[lzp->word1 * 7 + lzp->word0 * 11 + c0 + pc * 37];
    }
    r <<= 8;
    sp[0] = &cp[0][c0];
    int pr = (*stretch)(sm[0].p( * sp[0]));
    for (int i = 1; i < N; ++i) {
      sp[i] = &cp[i][i < 4 ? c0 : nibble];
      int st =* sp[i];
      pr = (m[i - 1].pp(pr, (*stretch)(sm[i].p(st)), st + r) * 3 + pr) >> 2;
    }
    pr = (a1.pp(512, pr * 2, c0 + pc * 256 & 0xffff) * 3 + pr) >> 2;  // Adjust prediction
    pr = (a2.pp(512, pr * 2, c4 << 8 & 0xff00 | c0) * 3 + pr) >> 2;
    pr = (a3.pp(512, pr * 2, c4 * 3 + c0 & 0xffff) * 3 + pr) >> 2;
    return (*squash)(pr);
  }
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda Code()
// .....................................................................................................................
INT32 __fastcall Encoder::Code(INT32 y) {
	int p = predictor->p();
	p += p < 2048;
	UINT32 xmid = x1 + ((x2 - x1) >> 12) * p + (((x2 - x1 & 0xfff) * p) >> 12);
	if (mode == DECOMPRESS) y = x <= xmid;
	y ? (x2 = xmid) : (x1 = xmid + 1);
	predictor->update(y);
	while (((x1 ^ x2) & 0xff000000) == 0) {  // pass equal leading bytes of range
		if (mode == COMPRESS) archive->AddBYTE(x2 >> 24);//buf[csize++] = x2 >> 24;
		x1 <<= 8;
		x2 = (x2 << 8) + 255;
		if (mode == DECOMPRESS) x = (x << 8) + archive->GetBYTE();
	}
	return y;
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda Count()
// .....................................................................................................................
void __fastcall Encoder::Count() {
	++usize;
	//if (csize > BUFSIZE - 256) Flush();
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
Encoder::~Encoder() {
	delete predictor;
	delete lzp;
	delete ilog;
	delete stretch;
	delete squash;
}
// .....................................................................................................................
// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
Encoder::Encoder(Mode m, IBitStream* f, LZP *pLZP): mode(m), archive(f), x1(0), x2(0xffffffff), x(0), usize(0), csize(0), usum(0), csum(0) {
	squash = new Squash();
	stretch = new Stretch();
	ilog = new Ilog();
	lzp = pLZP == NULL ? new LZP() : pLZP;
	predictor = new Predictor(lzp);
	if (mode == DECOMPRESS) {  // x = first 4 bytes of archive
		for (INT32 i = 0; i < 4; ++i) x = (x << 8) + (archive->GetBYTE() & 255);
		csize = 4;
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Metoda Flush()
// .....................................................................................................................
void Encoder::Flush() {
  if (mode == COMPRESS) {
	archive->AddBYTE(x1 >> 24);
	archive->AddBYTE(255);
	archive->AddBYTE(255);
	archive->AddBYTE(255);
    usum += usize;
    csum += csize + 10;
    x1 = x = usize = csize = 0;
    x2 = 0xffffffff;
  }
}
// .....................................................................................................................

// .....................................................................................................................
// Kompresia IBitStream
// .....................................................................................................................
INT32 __fastcall CompressBitStream(IBitStream *pSource, IBitStream *Result, LZP *pLZP) {
	
	INT32 InLength = pSource->GetCurrentLength();
	pSource->Rewind();

    int c;
    Encoder e(COMPRESS, Result, pLZP); // Inicializujem objekt
	for (int i = 0; i < InLength; i++) {
		c = pSource->Data[i]; // Ziskam aktualny bajt
		int cp = e.lzp->c();
		if (c == cp) e.Code(1);
		else {
			for (int i = 8; i >= 0; --i) e.Code((c >> i) & 0x1);
		}
		e.Count();
		e.lzp->update(c);
    }
    e.Flush();

	return Result->GetCurrentLength();
}
// .....................................................................................................................
// .....................................................................................................................
// Dekompresia IBitStream
// .....................................................................................................................
INT32 __fastcall DecompressBitStream(IBitStream *pSource, INT32 pSourceLength, IBitStream *Result, LZP *pLZP) {
	
	INT32 InLength = pSourceLength;//pSource->GetCurrentLength();
	pSource->Rewind();

	Encoder e(DECOMPRESS, pSource, pLZP);
	while (InLength--) {
		int c = e.lzp->c();
		if (e.Code()==0) {
			c=1;
			while (c<256) c+=c+e.Code();
			c&=255;
		}
		Result->AddBYTE(c);
		e.lzp->update(c);
	}
	return Result->GetCurrentLength();
}
// .....................................................................................................................
